package com.xishu.controller;

import com.xishu.annotation.valid.MethodValidator;
import com.xishu.aop.Validator;
import com.xishu.bo.*;
import com.xishu.bo.response.Report;
import com.xishu.config.Config;
import com.xishu.config.Constant;
import com.xishu.dao.jpa.OrderDao;
import com.xishu.entity.Company;
import com.xishu.entity.LockOrderCashOrOrderItem;
import com.xishu.entity.User;
import com.xishu.entity.customer.Customer;
import com.xishu.entity.customer.ScoreHistory;
import com.xishu.entity.customer.VipRenewMode;
import com.xishu.entity.oplog.OrderOpLog;
import com.xishu.entity.order.*;
import com.xishu.entity.shop.*;
import com.xishu.exception.ResponseStatusException;
import com.xishu.response.ResponseData;
import com.xishu.response.ResponseStatus;
import com.xishu.response.VerifyUtil;
import com.xishu.service.*;
import com.xishu.service.order.base.OrderService;
import com.xishu.service.order.callback.OrderCallBackService;
import com.xishu.service.order.create.CreateOrderChain;
import com.xishu.service.order.front.query.QueryOrderChain;
import com.xishu.service.order.orderItem.OrderItemChain;
import com.xishu.service.print.PrinterService;
import com.xishu.socket.WebSocketService;
import com.xishu.util.*;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang.StringUtils;
import org.apache.logging.log4j.ThreadContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.web.bind.annotation.*;

import java.io.File;
import java.util.*;
import java.util.stream.Collectors;

import static com.xishu.config.Constant.*;
import static com.xishu.response.ResponseStatus.*;

@RestController
@Api(description = "订单管理")
public class OrderController implements Tools {
    private static Logger logger = LoggerFactory.getLogger(OrderController.class);

    private CommonService commonService = CommonService.getInstance();
    private LockService localLockService = LockService.getInstance();
    private OrderService orderService = OrderService.getInstance();
    private ItemService itemService = ItemService.getInstance();
    private Config config = Config.getInstance();
    private FoodTableService foodTableService = FoodTableService.getInstance();
    private PayTypeService payTypeService = PayTypeService.getInstance();


    @ApiOperation(value = "创建订单")
    @PostMapping(value = ("/order"), produces = ("application/json;charset=UTF-8"))
    @MethodValidator(mustLogin = false, duplicationRequestCheck = true)
    public ResponseData create(@RequestBody Order order) throws Exception {
        logger.info("will create order");
        Long shopId = order.getShopId();
        VerifyUtil.verify(() -> shopId != null);
        Shop shop = ShopService.getInstance().findShopById(shopId);
        VerifyUtil.verify(() -> getBoolean(shop.getBusinessStatus()), ResponseStatus.SHOP_CLOSED);

        //如果没有场景值，并且是C端的订单，添加场景值
        if (order.getSceneId() == null) {
            logger.info("will find the scene id for the shop");
            Optional<Scene> sceneOptional = SceneService.getInstance().findSceneOptional(order.getShopId());
            if (sceneOptional.isPresent()) {
                Long sceneId = sceneOptional.get().getId();
                logger.info("will set scene id {}", sceneId);
                order.setSceneId(sceneId);
            } else {
                logger.info("can not find the match scene for shop {}", shopId);
            }
        }

        Order newOrder = new CreateOrderChain().createOrder(order);
        return commonService.createResponse(newOrder);
    }


    @ApiOperation(value = "查询营业流水表头总计")
    @PutMapping(value = ("/user/order/total"), produces = ("application/json;et=UTF-8"))
    @MethodValidator(mustLogin = false)
    public ResponseData findOrderTotal(@RequestBody Order order) throws Exception {
        //表头的总计数据，orderTotal且只有一个数据
        List<Order> orderTotal = orderService.findOrderTotal(order);
        return CommonService.getInstance().createResponse(orderTotal);
    }

    /**
     * 将菜品添加到购物车
     * <p>
     * C端在添加或者修改的时候，需要判断是否有一样的规格的菜，如果有
     *
     * @param reqOrder
     * @return
     */
    @PutMapping(value = {"/add/order/item", "/order/item"}, produces = ("application/json;charset=UTF-8"))
    @ApiOperation(value = "将菜品添加到购物车,如果没有则会添加，如果有了则会更新，如果是0，则会删除")
    @MethodValidator(mustLogin = false)
    public ResponseData orderItemAction(@RequestBody Order reqOrder) throws Exception {
        return new OrderItemChain(reqOrder).orderItemAction(reqOrder);
    }

    @ApiOperation(value = "修改订单")
    @PutMapping(value = ("/user/order/modify"), produces = ("application/json;charset=UTF-8"))
    @MethodValidator(checkShop = true)
    public ResponseData modify(@RequestBody Order order) throws Exception {
        VerifyUtil.verify(() -> isNotEmpty(order.getId()));
        VerifyUtil.verify(() -> isNotEmpty(order.getShopId()));
        //至少是餐厅经理
        User user = ServletUtil.getUser();
        VerifyUtil.verify(() -> new Validator().isShopManager(user));
        String lockKey = orderService.getLockKey(order);
        //打印清单不修改
        order.setSendPrinterList(null);
        //优惠卷不修改
        order.setParentTicketCodeList(null);

        Object lock = localLockService.getLock(lockKey);
        synchronized (lock) {
            if (order.getTipsFee() != null) {
                return modifyTips(order);
            }

            //如果是修改packageFee，那么重新计算价格,并且当前订单只修改packageFee
            if (order.getPackageFee() != null) {
                if (getDouble(order.getPackageFee()) < 0) {
                    order.setPackageFee(0d);
                }

                Order orderInDb = orderService.findOrder(order.getShopId(), order.getId());
                orderInDb.setPackageFee(order.getPackageFee());

                //重新计算用户需要支付的金额
                orderService.calPriceForB_C_END(orderInDb);
                commonService.save(orderInDb);

                orderService.showPrice(orderInDb, true);
                return commonService.createResponse(orderInDb);
            }

            //如果是在修改订单的类型，那么需要重新计算下价格
            if (order.getOrderType() != null) {
                Order orderInDb = orderService.findOrder(order.getShopId(), order.getId());
                orderInDb.setOrderType(order.getOrderType());
                orderService.calPriceForB_C_END(orderInDb);
                commonService.save(orderInDb);
                return commonService.createResponse(orderInDb);
            }

            //修改了可发送厨房列表
            if (isNotEmpty(order.getOrderItemDetailList()) && order.getOrderItemDetailList().get(0).getCanSendToKitchen() != null) {
                OrderItem orderItem = order.getOrderItemDetailList().get(0);
                VerifyUtil.verify(() -> orderItem.getId() != null);
                Order orderInDb = orderService.findOrder(order.getShopId(), order.getId());
                OrderItem orderItemInDb = orderService.filterOrderItemList(orderInDb, orderItem.getId());
                orderItemInDb.setCanSendToKitchen(orderItem.getCanSendToKitchen());
                commonService.save(orderInDb);
            }

            //修改支付方式
            if (order.getPayType() != null || order.getCashPayCallBack() != null) {
                orderService.modifyOrderPayType(order);
            }

            commonService.updateObject(order);
            Order orderInDb = orderService.findOrder(order.getShopId(), order.getId());
            return commonService.createResponse(orderInDb);
        }
    }

    /**
     * C端通过电话号码查询
     *
     * @param order
     * @return
     */
    @ApiOperation(value = "C端查询订单")
    @PutMapping(value = ("/customer/order"), produces = ("application/json;charset=UTF-8"))
    @MethodValidator(mustLogin = false)
    public ResponseData customerPhoneQuery(@RequestBody Order order) throws Exception {
        order.setSortField(new String[]{"createTime"});
        order.setSortType("DESC");
        order.setParentOrder(true);

        return new QueryOrderChain().queryOrder(order);
    }

    @ApiOperation(value = "查询订单")
    @PutMapping(value = ("/user/order"), produces = ("application/json;et=UTF-8"))
    @MethodValidator(mustLogin = false)
    public ResponseData search(@RequestBody Order order) throws Exception {
        return new QueryOrderChain().queryOrder(order);
    }

    /**
     * 获取支付类型
     *
     * @param orderInDb
     * @return
     */
    private List<Integer> getPayTypeList(Order orderInDb) {
        List<Integer> payTypeList = bitNumberToIntList(orderInDb.getPayTypeBit());
        return payTypeList;
    }

    @ApiOperation(value = "删除订单")
    @DeleteMapping(value = ("/user/order"), produces = ("application/json;charset=UTF-8"))
    @MethodValidator(checkShop = true)
    public ResponseData delete(@RequestBody Order order) throws Exception {
        Long shopId = order.getShopId();
        Long orderId = order.getId();
        VerifyUtil.verify(() -> shopId != null && orderId != null);

        Shop shopInDb = ShopService.getInstance().findShopById(shopId);
        //正在闭店的分店不能修改订单
        VerifyUtil.verify(() -> getInt(shopInDb.getStatus()) == SHOP_STATUS_NORMAL, ResponseStatus.SYSTEM_BUSY);

        //如果已结算或者已部分结账,不能删除
        Order orderInDb = orderService.findOrder(shopId, orderId);

        //删除的是子订单
        if (getBoolean(orderInDb.getSubOrder())) {
            orderService.deleteSubOrder(orderInDb);
            return ResponseData.emptyResponse();
        }

        //不是强制删除，那么需要校验
        if (!getBoolean(order.getForceDelete())) {
            VerifyUtil.verify(() -> !Arrays.asList(ORDER_STATUS_END, ORDER_STATUS_PART_CASH_CUSTOMIZED_PAYED, ORDER_STATUS_PART_FOOD_PAYED, ORDER_STATUS_PART_CASH_PAYED).contains(orderInDb.getStatus()), ResponseStatus.ORDER_PAYED_CAN_NOT_DELETE);
        }

        //删除的订单需要退回余量
        StockService.getInstance().deleteOrder(orderInDb);

        //查询落单审核，如果有未处理的，均按照拒绝来处理
        orderService.deleteAllAuditWithOrder(orderInDb);

        //添加删除的菜到报表
        List<OrderItem> orderItemList = getList(orderInDb.getOrderItemDetailList());
        for (OrderItem orderItem : orderItemList) {
            ReportService.getInstance().addDeleteOrderItem(orderItem.getId(), orderInDb);
        }

        List<FoodTable> foodTableList = new ArrayList<>();

        //删除订单，相当于取消开台
        if (orderService.isFoodTableOrder(orderInDb) && orderInDb.getParentOrderId() == null) {
            //找到餐桌
            foodTableList.addAll(FoodTableService.getInstance().findFoodTableListByOrder(orderInDb));
        }


        logger.info("will delete shop {} order {}", shopId, orderId);
        ResponseData responseData = commonService.delete(order);

        //删除订单日志
        OrderOpLogService orderOpLogService = OrderOpLogService.getInstance();
        OrderOpLog orderOpLog = orderOpLogService.content(orderInDb, DELETE_ORDER);
        orderOpLogService.deleteOrder(orderInDb, orderOpLog);

        //处理报表数据，将该订单的报表数据一起删除掉
        if (getBoolean(order.getForceDelete())) {
            ShopService.getInstance().deleteOrderReportData(shopId, orderId);
        }
        //删除订单，相当于取消开台
        if (orderService.isFoodTableOrder(orderInDb) && orderInDb.getParentOrderId() == null) {
            //餐桌除了模板之外，都是要删除的
            for (FoodTable foodTable : foodTableList) {
                logger.info("will delete food table  {}", foodTable.getId());
                foodTable.setFlagDelete(false);
                commonService.delete(foodTable);

                //处理模板
                FoodTableService.getInstance().dealTemplateFoodTable(foodTable);
            }
        }

        //强制删除的时候，需要重新处理下每日单号
        if (getBoolean(order.getForceDelete())) {
            OrderDao orderDao = BeanUtil.getBean(OrderDao.class);
            List<Order> orderList = orderDao.findTimeOrder(shopId, getLong(shopInDb.getOpenTime()), System.currentTimeMillis(), true, true, false);
            ShopService.getInstance().resetTimeNo(orderList, orderInDb.getTimeFix());

            //删除报表数据
            ReportService.getInstance().deleteRelatedReportData(order);
        }

        return responseData;
    }

    @ApiOperation(value = "撤消分钱分菜配置")
    @PutMapping(value = ("/user/order/part/cancel"), produces = ("application/json;charset=UTF-8"))
    @MethodValidator(mustLogin = false, duplicationRequestCheck = true)
    public ResponseData cancelPartConfig(@RequestBody Order reqOrder) throws Exception {
        Order order = orderService.findOrder(reqOrder);
        orderService.cancelPartOrder(order);
        return ResponseData.emptyResponse();
    }

    @ApiOperation(value = "更新支付订单类型,orderId, shopId, payType")
    @PutMapping(value = ("/user/order/pay/update/type"), produces = ("application/json;charset=UTF-8"))
    @MethodValidator(mustLogin = false, duplicationRequestCheck = true)
    public ResponseData updatePayOrderType(@RequestBody PayOrder reqPayOrder) throws Exception {
        Long orderId = reqPayOrder.getOrderId();
        Long shopId = reqPayOrder.getShopId();
        VerifyUtil.isEmpty(orderId, shopId);
        Order payOrder = orderService.findOrder(shopId, orderId);

        Integer payType = reqPayOrder.getPayType();
        VerifyUtil.verify(() -> payType != null);

        User user = ServletUtil.getUser();

        if (user != null) {
            boolean own = CompanyService.getInstance().ownCompany(user, payOrder.getCompanyId());
            VerifyUtil.verify(() -> own);
        } else {
            String sessionId = ServletUtil.getSessionId();
            boolean sameSession = equals(sessionId, payOrder.getSessionId());
            VerifyUtil.verify(() -> sameSession, ORDER_TIME_OUT);
        }

        payOrder.setPayType(payType);
        commonService.save(payOrder);
        return commonService.createResponse(payOrder);
    }

    @ApiOperation(value = "创建一个子订单")
    @PutMapping(value = ("/user/sub/order"), produces = ("application/json;charset=UTF-8"))
    @MethodValidator(duplicationRequestCheck = true)
    public ResponseData createSubOrder(@RequestBody SubOrder subOrder) throws Exception {
        Long orderId = subOrder.getOrderId();
        Long shopId = subOrder.getShopId();
        VerifyUtil.isEmpty(orderId, shopId);

        Order parentOrderInDb = orderService.findOrder(shopId, orderId, false);
        Order subOrderCreated = orderService.createSubOrder(parentOrderInDb);

        //分菜结账的单不需要有菜
        if (getInt(parentOrderInDb.getUserSelectPayType()) == USER_SELECT_PAY_TYPE_PART_FOOD) {
            subOrderCreated.setOrderItemDetailList(new ArrayList<>());
        }

        //自定义结账的单，处理未分配的钱
        if (getInt(parentOrderInDb.getUserSelectPayType()) == USER_SELECT_PAY_TYPE_PART_CASH_CUSTOMIZED || getInt(parentOrderInDb.getStatus()) == ORDER_STATUS_SETTING_PART_CASH) {
            if (parentOrderInDb.getUnassignCash() == null) {
                parentOrderInDb.setUnassignCash(parentOrderInDb.getUserCost());
            }
            List<Order> allSubOrders = orderService.findAllSubOrders(parentOrderInDb.getShopId(), parentOrderInDb.getId());

            //自定义结账，子订单大于19直接报错
            VerifyUtil.verify(() -> !(allSubOrders.size() > Constant.PART_CASH_MONEY_CREATE_SUB_THRESHOLD), PART_CASH_MONEY_CREATE_SUB_NOT_EXCEEDING_NUMBER);
            //第19个的时候，剩余金额和输入金额是否相等
            if (allSubOrders.size() == Constant.PART_CASH_MONEY_CREATE_SUB_THRESHOLD) {
                VerifyUtil.verify(() -> equals(getDouble(parentOrderInDb.getUnassignCash()), getDouble(subOrder.getUserCost())), PART_CASH_MONEY_CREATE_SUB_MAX_NUMBER);
            }

            VerifyUtil.verify(() -> subOrder.getUserCost() <= parentOrderInDb.getUnassignCash(), ASSIN_CASH_MORE_THAN_TOTAL);
            parentOrderInDb.setUnassignCash(showMoneyDouble(parentOrderInDb.getUnassignCash() - subOrder.getUserCost()));
            parentOrderInDb.setUserSelectPayType(USER_SELECT_PAY_TYPE_PART_CASH_CUSTOMIZED);

            //已经部分付款的情况下，不用再更新了
            if (parentOrderInDb.getStatus() <= ORDER_STATUS_PART_CASH_CUSTOMIZED) {
                parentOrderInDb.setStatus(ORDER_STATUS_PART_CASH_CUSTOMIZED);
            }
            commonService.save(parentOrderInDb);
            //自定义结账写入日志
            OrderOpLogService orderOpLogService = OrderOpLogService.getInstance();
            OrderOpLog orderOpLog = orderOpLogService.content(subOrderCreated, PART_CASH_MONEY);
            orderOpLogService.PartCashMoneyLog(subOrder, orderOpLog);


            //处理订单里面的菜
            //修改订单列表
            List<OrderItem> orderItemList = orderService.getOrderItemList(subOrderCreated);
            subOrderCreated.setOrderItemDetailList(orderItemList);
        }

        subOrderCreated.setUserCost(subOrder.getUserCost());
        subOrderCreated.setUserCost_b(subOrderCreated.getUserCost());
        subOrderCreated.setUserCost_c(subOrderCreated.getUserCost());

        subOrderCreated.setLeftCash(subOrder.getUserCost());
        subOrderCreated.setLeft_cash_b(subOrderCreated.getLeftCash());
        subOrderCreated.setLeft_cash_c(subOrderCreated.getLeftCash());

        subOrderCreated.setCostOrigin(subOrder.getUserCost());
        subOrderCreated.setCostOrigin_b(subOrderCreated.getCostOrigin());
        subOrderCreated.setCostOrigin_c(subOrderCreated.getCostOrigin());

        //如果有金额，按比例来处理税费
        if (Config.getInstance().isUsVersion() && getDouble(subOrder.getUserCost()) > 0) {
            Double consumptionTax = parentOrderInDb.getConsumptionTax();
            double tax = showMoneyDouble(subOrder.getUserCost() / parentOrderInDb.getUserCost()) * consumptionTax;
            subOrderCreated.setConsumptionTax(tax);
            subOrderCreated.setConsumptionTax_b(tax);
            subOrderCreated.setConsumptionTax_c(tax);
        }

        subOrderCreated.setServiceFee(null);
        subOrderCreated.setServiceFee_b(null);
        subOrderCreated.setServiceFee_c(null);

        commonService.directSave(subOrderCreated);
        //创建子订单写入日志
        OrderOpLogService orderOpLogService = OrderOpLogService.getInstance();
        OrderOpLog orderOpLog = orderOpLogService.content(subOrderCreated, CREATE_ORDER);
        orderOpLogService.createOrderLog(subOrderCreated, orderOpLog);


        return commonService.createResponse(subOrderCreated);
    }

    @ApiOperation(value = "菜在子订单当中移动")
    @PutMapping(value = ("/user/move/item/to/sub/order"), produces = ("application/json;charset=UTF-8"))
    @MethodValidator(duplicationRequestCheck = true)
    public ResponseData moveItem(@RequestBody List<MoveItem> moveItemList) throws Exception {
        MoveItem moveItemOne = moveItemList.get(0);

        Order fromSubOrder = orderService.findOrder(moveItemOne.getShopId(), moveItemOne.getFromOrderId());
        Order toSubOrder = orderService.findOrder(moveItemOne.getShopId(), moveItemOne.getToOrderId());

        Order cloneFromSubOrder = (Order) ClassUtil.clone(fromSubOrder);
        Order cloneToSubOrder = (Order) ClassUtil.clone(toSubOrder);

        List<OrderItem> specialItem = cloneFromSubOrder.getOrderItemDetailList().stream().filter(s -> {
            return equals(s.getItemId(), SPECIAL_ITEM);
        }).collect(Collectors.toList());
        //如果涉及特别菜，要做判断
        if (isNotEmpty(specialItem)) {
            Double fromCostOrigin = getDouble(cloneFromSubOrder.getCostOrigin());
            Double toCostOrigin = getDouble(cloneToSubOrder.getCostOrigin());
            Double sumMovePrice = 0d;
            for (MoveItem moveItem : moveItemList) {
                List<Long> orderItemIdList = moveItem.getOrderItemIdList();
                for (Long orderItemId : orderItemIdList) {
                    List<OrderItem> fromOrderItemDetailList = cloneFromSubOrder.getOrderItemDetailList();
                    Optional<OrderItem> orderItemOptional = fromOrderItemDetailList.stream().filter(p -> equals(p.getId(), orderItemId)).findFirst();
                    sumMovePrice += getDouble(orderItemOptional.get().getItemPrice());
                }
            }
            Double leftPrice = fromCostOrigin - sumMovePrice;
            Double toPrice = toCostOrigin + sumMovePrice;
            Boolean canMove = leftPrice >= 0 && toPrice >= 0;
            VerifyUtil.verify(() -> canMove, CAN_NOT_MOVE);
        }


        Object lock = localLockService.getLock(orderService.getLockKey(fromSubOrder.getShopId(), fromSubOrder.getParentOrderId()));
        synchronized (lock) {

            for (MoveItem moveItem : moveItemList) {
                moveItemToSubOrder(moveItem);
            }
        }

        return ResponseData.emptyResponse();
    }

    public ResponseData moveItemToSubOrder(MoveItem moveItem) throws Exception {
        Long shopId = moveItem.getShopId();
        Long fromOrderId = moveItem.getFromOrderId();
        Long toOrderId = moveItem.getToOrderId();
        List<Long> orderItemIdList = moveItem.getOrderItemIdList();
        VerifyUtil.isEmpty(shopId, fromOrderId, toOrderId, orderItemIdList);
        VerifyUtil.verify(() -> isNotEmpty(orderItemIdList));

        //必须要订单存在
        Optional<Order> fromOrderOptional = orderService.findOrderOptional(shopId, fromOrderId);
        VerifyUtil.verify(() -> fromOrderOptional.isPresent(), SUB_ORDER_DELETED);
        Optional<Order> toOrderOptional = orderService.findOrderOptional(shopId, toOrderId);
        VerifyUtil.verify(() -> toOrderOptional.isPresent(), SUB_ORDER_DELETED);
        Order fromOrder = fromOrderOptional.get();
        Order toOrder = toOrderOptional.get();

        //必须要是子订单
        VerifyUtil.verify(() -> getBoolean(fromOrder.getSubOrder()), SUB_ORDER_DELETED);
        VerifyUtil.verify(() -> getBoolean(toOrder.getSubOrder()), SUB_ORDER_DELETED);

        //必须要是相同的父订单
        VerifyUtil.verify(() -> equals(fromOrder.getParentOrderId(), toOrder.getParentOrderId()), SUB_ORDER_DELETED);

        //检查菜的个数是否匹配
        Map<Long, Integer> orderItemCountMap = new HashMap<>();
        for (Long orderItemId : orderItemIdList) {
            Integer count = orderItemCountMap.getOrDefault(orderItemId, 0);
            orderItemCountMap.put(orderItemId, count + 1);
        }

        //检查源订单里面是否有这么多的菜
        for (Map.Entry<Long, Integer> entry : orderItemCountMap.entrySet()) {
            Long orderItemId = entry.getKey();
            Integer moveCount = entry.getValue();

            long orderItemCountInOrder = fromOrder.getOrderItemDetailList().stream().filter(p -> equals(p.getId(), orderItemId)).count();
            //如果数量不对了，那么有可能是有其它服务员在操作
            VerifyUtil.verify(() -> orderItemCountInOrder >= moveCount, OTHER_USER_OPERATE_THE_ORDER);
        }

        //依次移动菜到另外一个订单
        for (Long orderItemId : orderItemIdList) {
            List<OrderItem> fromOrderItemDetailList = fromOrder.getOrderItemDetailList();
            Optional<OrderItem> orderItemOptional = fromOrderItemDetailList.stream().filter(p -> equals(p.getId(), orderItemId)).findFirst();

            VerifyUtil.verify(() -> orderItemOptional.isPresent(), SUB_ORDER_DELETED);
            //从列表里面删除
            for (int i = 0; i < fromOrderItemDetailList.size(); i++) {
                OrderItem orderItem = fromOrderItemDetailList.get(i);
                if (equals(orderItem.getId(), orderItemId)) {
                    fromOrderItemDetailList.remove(i);
                    break;
                }
            }

            //再添加另外一个订单里面
            List<OrderItem> toOrderItemDetailList = toOrder.getOrderItemDetailList();
            toOrderItemDetailList.add(orderItemOptional.get());

        }

        orderService.calPriceForB_C_END(fromOrder);
        orderService.calPriceForB_C_END(toOrder);
        //分别保存
        commonService.save(fromOrder);
        commonService.save(toOrder);

        //移动菜的日志
        OrderOpLogService orderOpLogService = OrderOpLogService.getInstance();
        OrderOpLog orderOpLog = orderOpLogService.content(fromOrder, MOVE_ITEM_TO_SUB_ORDER);
        orderOpLogService.moveItemToOrderLog(moveItem, toOrder, orderOpLog);


        return ResponseData.emptyResponse();
    }

    @ApiOperation(value = "创建一个充值支付订单")
    @MethodValidator(mustLogin = false, duplicationRequestCheck = true)
    @PutMapping(value = ("/user/recharge/order/pay"), produces = ("application/json;charset=UTF-8"))
    public ResponseData createRechargeOrderPay(@RequestBody Order order) throws Exception {
        VerifyUtil.isEmpty(order.getCustomerId(), order.getShopId(), order.getRechargeItemId());
        OrderService os = OrderService.getInstance();
        Shop shop = ShopService.getInstance().findShopById(order.getShopId());
        //为订单默认赋值
        os.orderDefaultValue(order, shop);
        //生成支付订单
        Order payOrder = orderService.createRechargePayOrder(order);
        //计算金额
        RechargeItem rechargeItem = commonService.findEntity(order.getRechargeItemId(), RechargeItem.class);
        Double userCost = rechargeItem.getUseCost();
        //设置金额
        payOrder.setUserCost(userCost);
        payOrder.setUserCostLong(showMoneyDouble(getDouble(payOrder.getUserCost()) * 100).longValue());
        payOrder.setUserCost_c(userCost);
        payOrder.setUserCost_b(userCost);
        logger.info("recharge_pay_order is end shopId is {},orderId is {}", payOrder.getShopId(), payOrder.getId());
        return commonService.createObject(payOrder);
    }


    @ApiOperation(value = "创建一个支付订单")
    @MethodValidator(mustLogin = false, duplicationRequestCheck = true)
    @PutMapping(value = ("/user/order/pay"), produces = ("application/json;charset=UTF-8"))
    public ResponseData createPayOrder(@RequestBody PayOrder reqPayOrder) throws Exception {
        VerifyUtil.verify(() -> reqPayOrder.getOrderId() != null && reqPayOrder.getShopId() != null);
        Object lock = localLockService.getLock(orderService.getLockKey(reqPayOrder.getShopId(), reqPayOrder.getOrderId()));
        synchronized (lock) {
            Order parentOrderInDb = orderService.findOrder(reqPayOrder.getShopId(), reqPayOrder.getOrderId(), false);
            VerifyUtil.verify(() -> !getBoolean(parentOrderInDb.getExpiredOrder()), ResponseStatus.ORDER_EXPIRED);
            User user = ServletUtil.getUser();
            //全部結賬日誌,这里只记录b端全部结账
            if (getBoolean(parentOrderInDb.getParentOrder()) && isNotEmpty(user)){
                //全部结账日志
                OrderOpLogService orderOpLogService = OrderOpLogService.getInstance();
                OrderOpLog orderOpLog = orderOpLogService.content(parentOrderInDb, USER_ALL_PAY);
                orderOpLogService.userAllPay(parentOrderInDb, orderOpLog);
            }

            //如果是C端的请求，并且当前订单已经被B端锁定了，那么报错
            Long userId = ServletUtil.getUserId();
            if (userId == null && getBoolean(parentOrderInDb.getLockByWaiter())) {
                VerifyUtil.throwError(ResponseStatus.WAITER_OPERATE_THE_ORDER);
            }

            if (isNotEmpty(reqPayOrder.getTicketCodeList())) {
                List<String> ticketCodeList = reqPayOrder.getTicketCodeList();
                Long shopId = reqPayOrder.getShopId();
                Shop shop = ShopService.getInstance().findShopById(shopId);
                Long companyId = shop.getCompanyId();
                DiscountTicketService discountTicketService = DiscountTicketService.getInstance();

                //校验优惠券，要没有被使用的优惠券才可以
                List<String> usedTicketList = ticketCodeList.stream().filter(code -> {
                    DiscountTicket ticket = discountTicketService.findTicketByCode(code, companyId);

                    if (ticket == null) {
                        return true;
                    }

                    return getBoolean(ticket.getUsed());
                }).collect(Collectors.toList());

                //有已被使用的优惠券
                if (isNotEmpty(usedTicketList)) {
                    VerifyUtil.verify(() -> false, DISCOUNT_TICKET_USED, StringUtils.join(usedTicketList, ","));
                }
            }

            int userSelectPayType = getUserSelectPayType(parentOrderInDb);
            int orderStatus = getInt(parentOrderInDb.getStatus());

            //如果使用了优惠券，那么判断优惠券是否可以
            if (isNotEmpty(reqPayOrder.getTicketCodeList())) {
                Long shopId = reqPayOrder.getShopId();
                Company company = CompanyService.getInstance().findCompanyByShopId(shopId);

                List<String> ticketCodeList = reqPayOrder.getTicketCodeList();
                List<String> usedCodeList = ticketCodeList.stream().filter(code -> {
                    DiscountTicket discountTicket = null;
                    try {
                        discountTicket = DiscountTicketService.getInstance().findTicketByCode(code, company.getId());
                    } catch (Exception e) {
                        logger.error("e", e);
                    }

                    if (discountTicket == null) {
                        return false;
                    }

                    return getBoolean(discountTicket.getUsed());
                }).collect(Collectors.toList());

                List<String> errorCodeList = usedCodeList.stream().map(code -> String.format("[%s]", code)).collect(Collectors.toList());

                VerifyUtil.verify(() -> isEmpty(usedCodeList), DISCOUNT_TICKET_USED, " " + StringUtils.join(errorCodeList, " ") + " ");
            }

            //一个人结账，直接返回,或者是外卖
            if (userSelectPayType == USER_SELECT_PAY_TYPE_ONE_USER || parentOrderInDb.getOrderType() == ORDER_TYPE_TAKE_OUT || parentOrderInDb.getOrderType() == ORDER_TYPE_QUICK_FOOD) {
                return oneUserPayForSubOrder(reqPayOrder, parentOrderInDb);
            } else if (userSelectPayType == Constant.USER_SELECT_PAY_TYPE_PART_CASH_PER_PERSON) {
                return partCashForPayOrder(reqPayOrder, parentOrderInDb);
            } else if (userSelectPayType == Constant.USER_SELECT_PAY_TYPE_PART_CASH_CUSTOMIZED || orderStatus == ORDER_STATUS_SETTING_PART_CASH) {
                return customizedPartCashForSubOrder(reqPayOrder, parentOrderInDb);
            } else if (userSelectPayType == Constant.USER_SELECT_PAY_TYPE_PART_FOOD) {
                return partFoodForSubOrder(reqPayOrder, parentOrderInDb);
            } else {
                VerifyUtil.throwError(ResponseStatus.ORDER_CANCEL_CONFIG_PAY_TYPE);
            }

            return ResponseData.emptyResponse();
        }
    }

    /**
     * 创建分菜付款的时候，不再校验数量，只在前面的时候校验数量
     * <p>
     * 分菜付款
     *
     * @param reqPayOrder
     * @param orderInDb
     * @return
     * @throws Exception
     */
    private ResponseData partFoodForSubOrder(PayOrder reqPayOrder, Order orderInDb) throws Exception {
        if (ServletUtil.getUserId() == null) {
            return customerPartFoodForSubOrder(reqPayOrder, orderInDb);
        } else {
            return waiterPartFoodForSubOrder(reqPayOrder);
        }
    }

    /**
     * 服务员分菜
     *
     * @param reqPayOrder
     * @return
     * @throws Exception
     */
    private ResponseData waiterPartFoodForSubOrder(PayOrder reqPayOrder) throws Exception {
        Long shopId = reqPayOrder.getShopId();
        Long orderId = reqPayOrder.getOrderId();
        Order subOrder = orderService.findOrder(shopId, orderId);
        VerifyUtil.verify(() -> getBoolean(subOrder.getSubOrder()));

        Order payOrder = orderService.createPayOrder(subOrder);
        payOrder.setCustomerId(reqPayOrder.getCustomerId());
        payOrder.setPayType(reqPayOrder.getPayType());
        payOrder.setSubOrderId(subOrder.getSubOrderId());
        payOrder.setTicketCodeList(reqPayOrder.getTicketCodeList());
        orderService.calPrice(payOrder, false);

        //eft支付
        payOrder.setEftPay(reqPayOrder.getEftPay());

        payOrder.setUserCostLong(showMoneyDouble(getDouble(payOrder.getUserCost()) * 100).longValue());
        //订单的原始价格再加上服务费
        payOrder.setPayOrderCost(showMoneyDouble(getDouble(payOrder.getUserCost()) + getDouble(payOrder.getDiscountFee())));
        DiscountTicketService.getInstance().bindTicketToSession(payOrder);
        return commonService.createObject(payOrder);
    }

    /**
     * 会员分菜
     *
     * @param reqPayOrder
     * @param orderInDb
     * @return
     * @throws Exception
     */
    private ResponseData customerPartFoodForSubOrder(PayOrder reqPayOrder, Order orderInDb) throws Exception {
        logger.info("will create food sub order, req pay order is {}", reqPayOrder);
        //分菜结账
        VerifyUtil.verify(() -> isNotEmpty(reqPayOrder.getOrderItemDetailList()));
        List<OrderItem> orderItemList = generateOrderItemList(reqPayOrder, orderInDb);

        Order subOrder = orderService.createPayOrder(orderInDb);
        subOrder.setCustomerId(reqPayOrder.getCustomerId());
        subOrder.setPayType(reqPayOrder.getPayType());
        subOrder.setSubOrderId(reqPayOrder.getSubOrderId());
        subOrder.setOrderItemDetailList(orderItemList);
        subOrder.setTicketCodeList(reqPayOrder.getTicketCodeList());
        orderService.calPrice(subOrder, false);

        subOrder.setUserCostLong(showMoneyDouble(getDouble(subOrder.getUserCost()) * 100).longValue());

        //订单的原始价格再加上服务费
        subOrder.setPayOrderCost(showMoneyDouble(getDouble(subOrder.getUserCost()) + getDouble(subOrder.getDiscountFee())));
        //设置c端分菜和分钱结账时显示应付的价格
        subOrder.setUserCost_c(subOrder.getUserCost());

        DiscountTicketService.getInstance().bindTicketToSession(subOrder);
        commonService.createObject(subOrder);
        return commonService.createResponse(subOrder);
    }

    private List<OrderItem> verifySub(PayOrder reqPayOrder, Order order, Long orderId, Long shopId) throws ResponseStatusException {
        List<Order> orderList = orderService.findOrderByParentId(orderId, shopId);
        //本次支付的菜品数据，不能大于未结账的菜品的数量
        HashMap<Long, Long> orderListOrderItemIdCountLockMap = new HashMap<>();

        orderList.forEach(o -> {
            for (OrderItem orderItem : getList(o.getOrderItemDetailList())) {
                Long orderItemCount = orderListOrderItemIdCountLockMap.getOrDefault(orderItem.getId(), 0L);
                orderListOrderItemIdCountLockMap.put(orderItem.getId(), orderItemCount + orderItem.getCount());
            }
        });

        //请求列表里面的的支付数量
        HashMap<Long, Long> reqOrderItemIdCountMap = new HashMap<>();

        for (OrderItem orderItem : reqPayOrder.getOrderItemDetailList()) {
            Long orderItemCount = reqOrderItemIdCountMap.getOrDefault(orderItem.getId(), 0L);
            reqOrderItemIdCountMap.put(orderItem.getId(), orderItemCount + orderItem.getCount());
        }

        //如果某一个菜已经没有余额的数量去支付了，那么提示对应的ID，并返回菜已经有客人支付了或者正在支付
        HashMap<Long, Long> orderItemIdCountMap = new HashMap<>();

        for (OrderItem orderItem : getList(order.getOrderItemDetailList())) {
            Long orderItemCount = orderItemIdCountMap.getOrDefault(orderItem.getId(), 0L);
            orderItemIdCountMap.put(orderItem.getId(), orderItemCount + orderItem.getCount());
        }

        //正在付款的数量，加上历史付款的数量，不能超过总单的数量
        for (Map.Entry<Long, Long> orderItemMap : reqOrderItemIdCountMap.entrySet()) {
            Long orderItemId = orderItemMap.getKey();
            Long payedCount = orderListOrderItemIdCountLockMap.getOrDefault(orderItemId, 0L);
            VerifyUtil.verify(() -> orderItemMap.getValue() + payedCount <= orderItemIdCountMap.get(orderItemId), ResponseStatus.OTHER_USER_PAYING_THE_ITEM);
        }

        //将菜品找出来，然后计算重新给数量
        List<OrderItem> orderItemList = reqPayOrder.getOrderItemDetailList().stream().map(orderItem -> {
            Optional<OrderItem> orderItemOptional = getList(order.getOrderItemDetailList()).stream().filter(p -> p.getId().longValue() == orderItem.getId().longValue()).findFirst();

            if (orderItemOptional.isPresent()) {
                orderItemOptional.get().setCount(orderItem.getCount());
                return orderItemOptional.get();
            } else {
                return null;
            }
        }).collect(Collectors.toList());

        long emptyCount = orderItemList.stream().filter(p -> p == null).count();

        VerifyUtil.verify(() -> emptyCount == 0, ResponseStatus.SYSTEM_ERROR);
        return orderItemList;
    }

    /**
     * 获取分菜付款的列表
     *
     * @param reqPayOrder
     * @param order
     * @return
     * @throws ResponseStatusException
     */
    public List<OrderItem> generateOrderItemList(PayOrder reqPayOrder, Order order) throws ResponseStatusException {
        List<OrderItem> orderItemList = reqPayOrder.getOrderItemDetailList().stream().map(orderItem -> {
            Optional<OrderItem> orderItemOptional = getList(order.getOrderItemDetailList()).stream().filter(p -> p.getId().longValue() == orderItem.getId().longValue()).findFirst();

            if (orderItemOptional.isPresent()) {
                orderItemOptional.get().setCount(orderItem.getCount());
                return orderItemOptional.get();
            } else {
                return null;
            }
        }).collect(Collectors.toList());

        long emptyCount = orderItemList.stream().filter(p -> p == null).count();

        VerifyUtil.verify(() -> emptyCount == 0, ResponseStatus.SYSTEM_ERROR);
        return orderItemList;
    }

    /**
     * 自定义分钱付款
     *
     * @param reqPayOrder
     * @param order
     * @return
     * @throws Exception
     */
    private ResponseData customizedPartCashForSubOrder(PayOrder reqPayOrder, Order order) throws Exception {
        if (ServletUtil.getUserId() == null) {
            //自定义，需要用户输入金额
            VerifyUtil.verify(() -> getDouble(reqPayOrder.getUserCost()) > 0);
            Order subOrder = orderService.createPayOrder(order);
            subOrder.setCustomerId(reqPayOrder.getCustomerId());
            subOrder.setPayType(reqPayOrder.getPayType());
            subOrder.setUserCost(reqPayOrder.getUserCost());
            subOrder.setLeftCash(reqPayOrder.getUserCost());
            subOrder.setCostOrigin(subOrder.getUserCost());

            //实际用户输入的是多少钱，就抵多少钱
            subOrder.setTicketCodeList(reqPayOrder.getTicketCodeList());
            orderService.calPrice(subOrder, true);
            //添加打折费用
            subOrder.setPayOrderCost(showMoneyDouble(subOrder.getUserCost()) + getDouble(subOrder.getDiscountFee()));
            subOrder.setUserCostLong(showMoneyDouble(getDouble(subOrder.getUserCost()) * 100).longValue());

            //设置c端分菜和分钱结账时显示应付的价格
            subOrder.setUserCost_c(subOrder.getUserCost());

            DiscountTicketService.getInstance().bindTicketToSession(subOrder);
            commonService.createObject(subOrder);

            //父订单的用户选择的支付方式需要修改成自定义分钱结账
            order.setUserSelectPayType(Constant.USER_SELECT_PAY_TYPE_PART_CASH_CUSTOMIZED);
            order.setStatus(ORDER_STATUS_PART_CASH_CUSTOMIZED);
            commonService.save(order);
            return commonService.createResponse(subOrder);
        } else {
            Long shopId = reqPayOrder.getShopId();
            Long orderId = reqPayOrder.getOrderId();
            Order subOrder = orderService.findOrder(shopId, orderId);
            VerifyUtil.verify(() -> getBoolean(subOrder.getSubOrder()));

            Order payOrder = orderService.createPayOrder(subOrder);
            payOrder.setCustomerId(reqPayOrder.getCustomerId());
            payOrder.setPayType(reqPayOrder.getPayType());
            payOrder.setUserCost(subOrder.getUserCost());
            payOrder.setLeftCash(subOrder.getUserCost());
            payOrder.setCostOrigin(subOrder.getUserCost());

            //eft支付
            payOrder.setEftPay(reqPayOrder.getEftPay());

            //实际用户输入的是多少钱，就抵多少钱
            payOrder.setTicketCodeList(reqPayOrder.getTicketCodeList());
            orderService.calPrice(payOrder, true);

            //添加打折费用
            payOrder.setPayOrderCost(getDouble(payOrder.getUserCost()) + getDouble(payOrder.getDiscountFee()));
            payOrder.setUserCostLong(showMoneyDouble(getDouble(payOrder.getUserCost()) * 100).longValue());

            commonService.directSave(payOrder);
            return commonService.createResponse(payOrder);
        }
    }

    /**
     * 按人数分钱付款
     *
     * @param reqPayOrder
     * @param order
     * @return
     * @throws Exception
     */
    private ResponseData partCashForPayOrder(PayOrder reqPayOrder, Order order) throws Exception {
        if (ServletUtil.getUserId() == null) {
            return customerPartCashForSubOrder(reqPayOrder, order);
        } else {
            return waiterPartCashForPayOrder(reqPayOrder);
        }
    }

    /**
     * 服务员端按人数分钱付款
     *
     * @param reqPayOrder
     * @return
     * @throws Exception
     */
    private ResponseData waiterPartCashForPayOrder(PayOrder reqPayOrder) throws Exception {
        Long orderId = reqPayOrder.getOrderId();
        Long shopId = reqPayOrder.getShopId();
        Order subOrder = orderService.findOrder(shopId, orderId);
        VerifyUtil.verify(() -> getBoolean(subOrder.getSubOrder()));
        Order payOrder = orderService.createPayOrder(subOrder);

        //eft支付
        payOrder.setEftPay(reqPayOrder.getEftPay());

        payOrder.setCustomerId(reqPayOrder.getCustomerId());
        payOrder.setPayType(reqPayOrder.getPayType());
        payOrder.setUserCost(subOrder.getUserCost());
        payOrder.setTicketCodeList(reqPayOrder.getTicketCodeList());
        orderService.calPrice(payOrder, true); //添加打折费用
        payOrder.setPayOrderCost(showMoneyDouble(showMoneyDouble(payOrder.getUserCost()) + showMoneyDouble(payOrder.getDiscountFee())));

        //显示的long值，钱 *  100来做
        payOrder.setUserCostLong(showMoneyDouble(getDouble(payOrder.getUserCost()) * 100).longValue());

        DiscountTicketService.getInstance().bindTicketToSession(payOrder);
        return commonService.createObject(payOrder);
    }

    /**
     * C端按人数分钱
     *
     * @param reqPayOrder
     * @param order
     * @return
     * @throws Exception
     */
    private ResponseData customerPartCashForSubOrder(PayOrder reqPayOrder, Order order) throws Exception {
        //分钱结账，如果是均分，可以直接生成子订单
        Order subOrder = orderService.createPayOrder(order);
        subOrder.setCustomerId(reqPayOrder.getCustomerId());
        subOrder.setPayType(reqPayOrder.getPayType());
        //查询子订单个数，以订单数量是3为例，1，2可以直接均分，3需要创建剩余的金额
        //如果是超过订单个数，以3为例，那么就要看余额金额是多少，如果刚好够，以及大于平均的钱 + 1，都是均分的钱，如果是均分的钱 + 1，那么就是均分的钱
        Double userCost = OrderService.getInstance().lockThePartCash(order);
        subOrder.setUserCost(userCost);
        subOrder.setLeftCash(subOrder.getUserCost());
        subOrder.setCostOrigin(subOrder.getUserCost());
        subOrder.setTicketCodeList(reqPayOrder.getTicketCodeList());
        orderService.calPrice(subOrder, true);

        //添加打折费用
        subOrder.setPayOrderCost(showMoneyDouble(showMoneyDouble(subOrder.getUserCost()) + showMoneyDouble(subOrder.getDiscountFee())));

        //显示的long值，钱 *  100来做
        subOrder.setUserCostLong(showMoneyDouble(getDouble(subOrder.getUserCost()) * 100).longValue());

        //设置c端分菜和分钱结账时显示应付的价格
        subOrder.setUserCost_c(subOrder.getUserCost());

        DiscountTicketService.getInstance().bindTicketToSession(subOrder);
        commonService.createObject(subOrder);
        return commonService.createResponse(subOrder);
    }

    /**
     * 一个人结账
     *
     * @param reqPayOrder
     * @param parentOrderInDb
     * @return
     * @throws Exception
     */
    private ResponseData oneUserPayForSubOrder(PayOrder reqPayOrder, Order parentOrderInDb) throws Exception {
        Order subOrder = orderService.createPayOrder(parentOrderInDb);
        subOrder.setCustomerId(reqPayOrder.getCustomerId());
        subOrder.setPayType(reqPayOrder.getPayType());

        //添加这次要使用的优惠券
        if (isNotEmpty(reqPayOrder.getTicketCodeList())) {
            subOrder.setTicketCodeList(reqPayOrder.getTicketCodeList());
        }

        //添加优惠卷后，实现的钱
        //不再重新计算价格
        //orderService.calPrice(subOrder, false);
        //一个人结账，付款后的金额等于剩下没有付完钱的金额
        //这里应该是B端的
        subOrder.setTipsFee(reqPayOrder.getTipsFee());
        orderService.calPrice(subOrder, false);

        //是否使用了eft支付
        subOrder.setEftPay(reqPayOrder.getEftPay());

        subOrder.setPayOrderCost(showMoneyDouble(parentOrderInDb.getLeftCash()));
        subOrder.setUserCostLong(showMoneyDouble(getDouble(subOrder.getUserCost()) * 100).longValue());
        subOrder.setUserCost_b(subOrder.getUserCost());
        subOrder.setUserCost_c(subOrder.getUserCost());
        subOrder.setLeft_cash_b(subOrder.getLeftCash());
        subOrder.setLeft_cash_c(subOrder.getLeftCash());
        DiscountTicketService.getInstance().bindTicketToSession(subOrder);
        commonService.createObject(subOrder);
        return commonService.createResponse(subOrder);
    }

    @ApiOperation(value = "修改订单为支付成功，只有登录账号才可以，并且必须要是经理以上的账号")
    @PutMapping(value = ("/user/order/call/back"), produces = ("application/json;charset=UTF-8"))
    @MethodValidator(mustLogin = true, duplicationRequestCheck = true)
    public ResponseData callBack(@RequestBody Order reqOrder) throws Exception {
        User user = ServletUtil.getUser();
        VerifyUtil.verify(() -> new Validator().isShopManager(user));
        VerifyUtil.verify(() -> reqOrder.getShopId() != null && reqOrder.getId() != null);
        Order order = orderService.findOrderCanFlagDelete(reqOrder);

        //有设置值的时候，才设置
        if (reqOrder.getPayType() != null) {
            order.setPayType(reqOrder.getPayType());
        }
        if (getBoolean(order.getRechargeOrder())) {
            OrderCallBackService.getInstance().dealCustomerRecharge(order);
            return ResponseData.emptyResponse();
        }
        if (getBoolean(order.getVipOrder())) {
            OrderCallBackService.getInstance().dealVipOrder(order);
            return ResponseData.emptyResponse();
        } else {
            //把打印机的ID带下去
            if (reqOrder.getPrinterId() != null) {
                order.setPrinterId(reqOrder.getPrinterId());
            }

            //如果是现金支付，需要把现金支付添加进去
            if (reqOrder.getCashPayCallBack() != null) {
                CashPay cashPayCallBack = reqOrder.getCashPayCallBack();
                cashPayCallBack.setOrderId(reqOrder.getId());
                VerifyUtil.verify(() -> cashPayCallBack.getPayCash() != null);

                //用户的退款金额由服务器计算
                double returnCash = showDouble(cashPayCallBack.getPayCash() - order.getUserCost());
                //退款必须大于等于0
                VerifyUtil.verify(() -> returnCash >= 0, SYSTEM_BUSY);
                cashPayCallBack.setReturnCash(returnCash);
                cashPayCallBack.setPayType(ORDER_PAY_TYPE_CASH);
                order.setCashPayCallBack(cashPayCallBack);
                order.setCashPayList(Arrays.asList(cashPayCallBack));
            }

            OrderCallBackService.getInstance().orderCallback(order);
            return ResponseData.emptyResponse();
        }
    }

    @ApiOperation(value = "取消订单,撤消订单")
    @PutMapping(value = ("/user/order/cancel/payType"), produces = ("application/json;charset=UTF-8"))
    @MethodValidator(mustLogin = false, duplicationRequestCheck = true)
    public ResponseData cancel(@RequestBody Order reqOrder) throws Exception {
        Long shopId = reqOrder.getShopId();
        Long orderId = reqOrder.getId();
        VerifyUtil.verify(() -> shopId != null && orderId != null);
        String lockKey = orderService.getLockKey(reqOrder);
        Object lock = localLockService.getLock(lockKey);

        synchronized (lock) {
            logger.info("will cancel order");
            Order orderInDb = orderService.findOrder(shopId, orderId);
            VerifyUtil.verify(() -> !getBoolean(orderInDb.getExpiredOrder()), ResponseStatus.ORDER_EXPIRED);

            reqOrder.setReturnCash(true);
            orderService.cancelOrder(shopId, orderId);
        }

        return ResponseData.emptyResponse();
    }

    @ApiOperation(value = "数据报表导出")
    @PutMapping(value = ("/user/order/export"), produces = ("application/json;charset=UTF-8"))
    public ResponseData export(@RequestBody ReportBo reportBo) throws Exception {
        ReportService reportService = ReportService.getInstance();
        logger.info("will query report {}", reportBo);
        VerifyUtil.verify(() -> reportBo.getType() != null);
        File file = reportService.exportData(reportBo);

        return commonService.createResponse(file.getName());
    }

    /**
     * 订单数据导出
     *
     * @param order
     * @return
     */
    @PutMapping(value = ("/user/order/excel/export"), produces = ("application/json;charset=UTF-8"))
    public ResponseData exportOrderData(@RequestBody Order order) throws Exception {
        //不分页
        order.setPageSize(null);
        order.setPageNumber(null);
        orderService.payTypeListToBitPayType(order);
        Long orderSize = commonService.count(order);
        VerifyUtil.verify(() -> orderSize <= 20000, ORDER_TOO_LARGE);
        List<Order> orderList = commonService.searchAll(order);
        List<Shop> shopList = new ArrayList<>();
        Order orderTotal = orderService.findOrderTotal(order).get(0);
        orderTotal.setShopName("总计");
        orderTotal.setShopName_zh("总计");
        orderTotal.setShopName_en("Total");
        orderList.add(orderTotal);
        if (config.isUsVersion()) {
            List<String> headerList = Arrays.asList("訂單ID/Order ID", "分店/Branch Name", "用餐時間/Time", "用餐人數/Number Of People", "消費金額/Sale", "税/Tax", "總計/Total", "小費/服務費 / Tips/Service Charge", "優惠金額/Total Discount", "付款金額/Total Payment", "支付方式/Payment Method", "失效訂單？/Expired Orders?", "已退款？/Refunded?");
            //订单数据格式转化
            List<List<Object>> orderDataList = orderList.stream().map(o -> {
                try {
                    List<Object> dataList = new ArrayList<>();

                    if (o.getShopName() != null && equals("总计", o.getShopName())) {
                        dataList.add(o.getTimeNo());
                        dataList.add(o.getShopName_zh() + "/" + o.getShopName_en());
                        dataList.add(o.getCreateDate());
                        dataList.add(getInt(o.getPersons()) > 0 ? o.getPersons() : "");
                        dataList.add(showMoneyDouble(getDouble(o.getCostOrigin()) - getDouble(o.getDiscountFee()) - getDouble(o.getTakeOutDiscountFee())));
                        dataList.add(showMoneyDouble(o.getConsumptionTax()));
                        dataList.add(showMoneyDouble(getDouble(o.getUserCost()) - getDouble(o.getTipsFee()) - getDouble(o.getServiceFee())));
                        dataList.add(showMoneyDouble(getDouble(o.getTipsFee()) + getDouble(o.getServiceFee())));
                        dataList.add(showMoneyDouble(o.getDiscountFee()));
                        CashPay cashPay = orderService.getCashPayList(order).get(0);
                        dataList.add(showMoneyDouble(cashPay.getPayCash()));
                        return dataList;
                    }
                    dataList.add(o.getTimeNo());
                    Shop shop = ShopService.getInstance().findShop(o.getShopId(), shopList);
                    dataList.add(shop.getName_zh() + "/" + shop.getName_en());
                    dataList.add(o.getCreateDate());
                    dataList.add(getInt(o.getPersons()) > 0 ? o.getPersons() : "");
                    dataList.add(showMoneyDouble(getDouble(o.getCostOrigin()) - getDouble(o.getDiscountFee()) - getDouble(o.getTakeOutDiscountFee())));
                    dataList.add(showMoneyDouble(o.getConsumptionTax()));
                    dataList.add(showMoneyDouble(getDouble(o.getUserCost()) - getDouble(o.getTipsFee()) - getDouble(o.getServiceFee())));
                    dataList.add(showMoneyDouble(getDouble(o.getTipsFee()) + getDouble(o.getServiceFee())));
                    dataList.add(showMoneyDouble(o.getDiscountFee()));

                    Double cashPayTotal = 0.0;
                    //实际支付的价格
                    for (CashPay cashPay : getList(o.getCashPayList())) {
                        Double payCash = null;
                        if (isNotEmpty(cashPay.getReturnCash())) {
                            payCash = cashPay.getPayCash() - cashPay.getReturnCash();
                        } else {
                            payCash = cashPay.getPayCash();
                        }
                        cashPayTotal = cashPayTotal + payCash;
                    }

                    dataList.add(showMoneyDouble(cashPayTotal));
                    dataList.add(getExportOrderPayType(o));
                    if (getBoolean(o.getExpiredOrder())) {
                        dataList.add("yes");
                    } else {
                        dataList.add("no");
                    }

                    if (getBoolean(o.getReturnCash())) {
                        dataList.add("yes");
                    } else {
                        dataList.add("no");
                    }

                    return dataList;
                } catch (Exception e) {
                    logger.error("e", e);
                }

                return null;
            }).collect(Collectors.toList());
            File file = ExcelUtil.getInstance().exportExcel(headerList, orderDataList.stream().filter(p -> p != null).collect(Collectors.toList()));


            ObjectService.getInstance().uploadObject(file);

            return commonService.createResponse(file.getName());
        }
        //香港版本
        List<String> headerList = Arrays.asList("訂單ID/Order ID", "分店/Branch Name", "用餐時間/Time", "用餐人數/Number Of People", "消費金額/ Total Sale", "優惠金額/Total Discount", "付款金額/Total Payment", "支付方式/Payment Method", "失效訂單？/Expired Orders?", "已退款？/Refunded?");
        //订单数据格式转化
        List<List<Object>> orderDataList = orderList.stream().map(o -> {
            try {
                List<Object> dataList = new ArrayList<>();
                if (o.getShopName() != null && equals("总计", o.getShopName())) {
                    dataList.add(o.getTimeNo());
                    dataList.add(o.getShopName_zh() + "/" + o.getShopName_en());
                    dataList.add(o.getCreateDate());
                    dataList.add(getInt(o.getPersons()) > 0 ? o.getPersons() : "");
                    dataList.add(showMoneyDouble(getDouble(o.getCostOrigin()) + getDouble(o.getServiceFee()) + getDouble(o.getPackageFee()) - getDouble(o.getTakeOutDiscountFee())));
                    dataList.add(showMoneyDouble(o.getDiscountFee()));
                    CashPay cashPay = orderService.getCashPayList(order).get(0);
                    dataList.add(showMoneyDouble(cashPay.getPayCash()));
                    return dataList;
                }
                dataList.add(o.getTimeNo());
                Shop shop = ShopService.getInstance().findShop(o.getShopId(), shopList);
                dataList.add(shop.getName_zh() + "/" + shop.getName_en());
                dataList.add(o.getCreateDate());
                dataList.add(getInt(o.getPersons()) > 0 ? o.getPersons() : "");
                dataList.add(showMoneyDouble(getDouble(o.getCostOrigin()) + getDouble(o.getServiceFee()) + getDouble(o.getPackageFee()) - getDouble(o.getTakeOutDiscountFee())));
                dataList.add(showMoneyDouble(o.getDiscountFee()));

                Double cashPayTotal = 0.0;
                //实际支付的价格
                for (CashPay cashPay : getList(o.getCashPayList())) {
                    Double payCash = null;
                    if (isNotEmpty(cashPay.getReturnCash())) {
                        payCash = cashPay.getPayCash() - cashPay.getReturnCash();
                    } else {
                        payCash = cashPay.getPayCash();
                    }
                    cashPayTotal = cashPayTotal + payCash;
                }
                dataList.add(showMoneyDouble(cashPayTotal));
                dataList.add(getExportOrderPayType(o));
                //是否是失效订单
                if (getBoolean(o.getExpiredOrder())) {
                    dataList.add("yes");
                } else {
                    dataList.add("no");
                }
                //是否退款
                if (getBoolean(o.getReturnCash())) {
                    dataList.add("yes");
                } else {
                    dataList.add("no");
                }

                return dataList;
            } catch (Exception e) {
                logger.error("e", e);
            }

            return null;
        }).collect(Collectors.toList());
        File file = ExcelUtil.getInstance().exportExcel(headerList, orderDataList.stream().filter(p -> p != null).collect(Collectors.toList()));

        ObjectService.getInstance().uploadObject(file);

        return commonService.createResponse(file.getName());
    }

    /**
     * 获取导出流水里面显示的支付类型
     *
     * @param order
     * @return
     */
    private String getExportOrderPayType(Order order) {
        List<Integer> payTypeList = getPayTypeList(order);

        //有支付方式，并且没有退款，那么就添加显示支付方式
        if (isNotEmpty(payTypeList)) {
            StringBuilder sb = new StringBuilder();
            List<String> payTypeStringList = payTypeList.stream().map(type -> LangService.getInstance().getPayType(Long.valueOf(type), false)).collect(Collectors.toList());
            sb.append(StringUtils.join(payTypeStringList, ","));

            //如果还有退款，那么就是部分退款
            if (getBoolean(order.getReturnCash())) {
                sb.append(",Partially Refunded");
            }

            //如果还有未付款的，那么添加未付款的信息
            if (getDouble(order.getLeftCash()) > 0) {
                sb.append(",Unpaid：");
                sb.append(showMoneyDouble(order.getLeftCash()));
            }

            return sb.toString();
        } else {
            //为空，说明已经全部退款了
            return "Refunded,Unpaid：" + showMoneyDouble(order.getLeftCash());
        }
    }

    @ApiOperation(value = "查询报表, 从类型是10开始的报表的分店值，均传shopIdList")
    @PutMapping(value = ("/user/order/report"), produces = ("application/json;charset=UTF-8"))
    public ResponseData queryReport(@RequestBody ReportBo reportBo) throws Exception {
        ReportService reportService = ReportService.getInstance();
        logger.info("will query report {}", reportBo);
        VerifyUtil.verify(() -> reportBo.getType() != null);

        if (reportBo.getShopId() != null) {
            Long shopId = reportBo.getShopId();
            boolean own = CompanyService.getInstance().ownCompanyByShopId(shopId);
            VerifyUtil.verify(() -> own);
        } else if (isNotEmpty(reportBo.getShopIdList())) {
            List<Long> shopIdList = reportBo.getShopIdList();
            for (Long shopId : shopIdList) {
                boolean own = CompanyService.getInstance().ownCompanyByShopId(shopId);
                VerifyUtil.verify(() -> own);
            }
        } else {
            VerifyUtil.verify(() -> reportBo.getCompanyId() != null, INVALID_PARAMETER);
        }
        //查询报表
        List<Report> reportData = reportService.getReportData(reportBo);

        return commonService.createResponse(reportData);
    }

    @ApiOperation(value = "将某一个菜发往厨房")
    @PutMapping(value = ("/send/printer"), produces = ("application/json;charset=UTF-8"))
    @MethodValidator(checkShop = true, checkShopId = true, duplicationRequestCheck = true)
    public ResponseData sendToPrinter(@RequestBody Order reqOrder) throws Exception {
        Long shopId = reqOrder.getShopId();
        Long orderId = reqOrder.getId();
        String lockKey = orderService.getLockKey(shopId, orderId);
        VerifyUtil.verify(() -> isNotEmpty(reqOrder.getSendPrinterList()));
        Object lock = localLockService.getLock(lockKey);

        synchronized (lock) {
            Order orderInDb = orderService.findOrder(shopId, orderId, false);
            VerifyUtil.verify(() -> !getBoolean(orderInDb.getExpiredOrder()), ResponseStatus.ORDER_EXPIRED);

            logger.info("find the order is {}", orderInDb);
            List<Long> orderItemIdList = getList(reqOrder.getSendPrinterList()).stream().map(p -> p.getOrderItemId()).collect(Collectors.toList());
            logger.info("orderItemIdList is {}", StringUtils.join(orderItemIdList, ","));

            //只发送未发送到厨房的列表
            List<Long> sendPrinterList = getList(orderInDb.getSendPrinterList()).stream().map(o -> o.getOrderItemId()).collect(Collectors.toList());
            orderItemIdList = orderItemIdList.stream().filter(orderItemId -> !sendPrinterList.contains(orderItemId.longValue())).collect(Collectors.toList());
            logger.info("after filter order item size is {}", orderItemIdList.size());

            if (isEmpty(orderItemIdList)) {
                logger.info("nothing to printer");
                return commonService.createResponse(orderInDb);
            }

            //确认订单的时候，锁定量处理
            List<Stock> stockList = StockService.getInstance().sendToKitchen(orderInDb, orderItemIdList);
            //存在余量不足的菜
            if (isNotEmpty(stockList)) {
                logger.info("some item not enough {}", stockList);
                return commonService.createResponse(stockList);
            }

            logger.info("all item left count is ok");

            //计算真实要发送到厨房里面的菜的数量
            DayReportService.getInstance().addSendKitchenNumber(orderInDb, orderItemIdList);

            //先打印，真实发送到厨房里面
            PrinterService.getInstance().printerOrderItemList(orderInDb, orderItemIdList);

            //如果以前发送过的，后面的再加粗
            List<SendPrinter> reqSendPrinterList = getList(reqOrder.getSendPrinterList());
            for (SendPrinter sendPrinter : reqSendPrinterList) {
                orderService.sendPrinter(orderInDb, sendPrinter);
            }

            //添加发送到厨房的标记
            orderInDb.setSendKitchen(true);
            commonService.save(orderInDb);

            //打印厨房订单日志
            OrderOpLogService kitchenOrderOpLogService = OrderOpLogService.getInstance();
            OrderOpLog kitchenOrderOpLog = kitchenOrderOpLogService.content(orderInDb, PRINT_KITCHEN_ORDER);
            kitchenOrderOpLogService.printKitchenOrder(orderInDb, kitchenOrderOpLog);


            //如果是桌上的订单，更新状态
            if (orderService.isFoodTableOrder(orderInDb)) {
                foodTableService.dealOrderFoodTableStatus(orderInDb);
            }

            //如果是B端需要打印小票，那么添加打印小票
            orderService.sendToKitchenPrintWholeOrderInB(orderInDb, reqOrder.getPrinterId(), true);

            return commonService.createResponse(orderInDb);
        }
    }

    /**
     * 计算价格
     *
     * @param order
     * @return
     */
    @ApiOperation(value = "计算订单的价格")
    @PutMapping(value = ("/order/cal/price"), produces = ("application/json;charset=UTF-8"))
    @MethodValidator(mustLogin = false)
    public ResponseData calPrice(@RequestBody Order order) throws Exception {
        VerifyUtil.verify(() -> order.getShopId() != null);
        try {
            boolean dependsCash = false;

            Order orderInDb = OrderService.getInstance().findOrder(order.getShopId(), order.getId());
            order.setOrderType(orderInDb.getOrderType());

            //寶子订单不处理金额
            if (getBoolean(orderInDb.getSubOrder())) {
                dependsCash = true;
                BeanUtils.copyProperties(orderInDb, order, "ticketCodeList", "customerId");
                ThreadContext.put(THREAD_LOCAL_VARIABLE_FE_CAL_PRICE, "true");
            } else if (getDouble(order.getUserCost()) > 0) {
                //也只有分钱结账的时候，需要用客户端传过来的钱来计算价格，其余不管是一个人结账还是分散结账，均需要按菜来结算
                dependsCash = true;
            }

            if (isNotEmpty(orderInDb.getPackageFee())) {
                order.setPackageFee(orderInDb.getPackageFee());
            }
            PriceResult priceResult = orderService.calPrice(order, dependsCash, false, false);
            return commonService.createResponse(priceResult);
        } finally {
            ThreadContext.remove(THREAD_LOCAL_VARIABLE_FE_CAL_PRICE);
        }
    }


    @ApiOperation(value = "清空购物车")
    @PutMapping(value = ("/order/clean"), produces = ("application/json;charset=UTF-8"))
    @MethodValidator(mustLogin = false, duplicationRequestCheck = true)
    public ResponseData cleanCard(@RequestBody Order order) throws Exception {
        Long shopId = order.getShopId();
        Long orderId = order.getId();
        VerifyUtil.verify(() -> shopId != null && orderId != null);
        return orderService.cleanOrder(shopId, orderId);
    }

    @ApiOperation(value = "绑定优惠卷")
    @PutMapping(value = ("/order/bind/discount/ticket"), produces = ("application/json;charset=UTF-8"))
    @MethodValidator(mustLogin = false)
    public ResponseData bindDiscountTicket(@RequestBody Order order) throws Exception {
        Long shopId = order.getShopId();
        Long orderId = order.getId();
        VerifyUtil.verify(() -> shopId != null && orderId != null);
        Long userId = ServletUtil.getUserId();
        String lockKey = orderService.getLockKey(order);
        Object lock = localLockService.getLock(lockKey);

        synchronized (lock) {
            //先查询是否有支付订单，如果有了，则不能再修改了
            boolean hasPayedOrder = orderService.hasPayedOrder(orderId, shopId);
            VerifyUtil.verify(() -> !hasPayedOrder, ResponseStatus.SOME_ORDER_MAY_PAYED);

            Order orderInDb = orderService.findOrder(shopId, orderId);
            List<String> parentTicketCodeList = getList(order.getParentTicketCodeList());
            orderInDb.setParentTicketCodeList(parentTicketCodeList);
            orderInDb.setTicketCodeList(parentTicketCodeList);

            //计算价格
            logger.info("will cal price");
            PriceResult priceResult = orderService.calPrice(orderInDb, false);
            //计算完价格之后，将参数清空
            orderInDb.setTicketCodeList(new ArrayList<>());
            VerifyUtil.verify(() -> orderService.orderPriceOk(priceResult), ResponseStatus.DISCOUNT_TICKET_CONFIG_WRONG);

            orderInDb.setUserCost(priceResult.getPrice());
            orderInDb.setCostOrigin(priceResult.getCostOrigin());
            orderInDb.setDiscountFee(priceResult.getDiscountFee());
            orderInDb.setServiceFee(priceResult.getServiceFee());
            orderInDb.setLeftCash(priceResult.getPrice());

            //如果是B端的操作，那么锁定订单
            if (userId != null) {
                orderInDb.setLockByWaiter(true);
            }

            //计算折扣力度
            if (isNotEmpty(order.getParentTicketCodeList())) {
                logger.info("will cal discount percent");
                Double discountPercent = 1 - showDouble(getDouble(priceResult.getDiscountFee()) / priceResult.getCostOrigin(), 5);
                logger.info("discount fee is {}, cost origin is {}, discount percent is {}", priceResult.getDiscountFee(), priceResult.getCostOrigin(), discountPercent);
                //给分菜结账的时候使用。
                //这种折扣，只算在分菜结账上面，分菜结账的时候才需要在总在订单上面打个折，然后在每个菜里面打折
                orderInDb.setDiscountPercent(discountPercent);
            }

            //如果是按人数分钱结账，那么修改分钱结账的配置
            PartCash partCash = orderInDb.getPartCash();

            if (partCash != null) {
                if (getInt(partCash.getPersons()) > 0) {
                    partCash.setPerCost(formatMoneyDouble(orderInDb.getUserCost() / partCash.getPersons()));
                }
            }

            //将优惠卷绑定的会话ID更新
            if (isNotEmpty(parentTicketCodeList)) {
                DiscountTicketService.getInstance().bindTicketToSession(orderInDb);
                //将优惠码统一设置到ticketCodeList里面
                orderInDb.setTicketCodeList(parentTicketCodeList);
            }

            commonService.save(orderInDb);
            return commonService.createResponse(orderInDb);
        }
    }

    @ApiOperation(value = "确认订单，确认过后的订单在B端可以看得到")
    @PutMapping(value = ("/order/confirm"), produces = ("application/json;charset=UTF-8"))
    @MethodValidator(mustLogin = false, duplicationRequestCheck = true)
    public ResponseData confirmOrder(@RequestBody Order order) throws Exception {
        Long shopId = order.getShopId();
        Long orderId = order.getId();
        VerifyUtil.verify(() -> shopId != null);
        VerifyUtil.verify(() -> orderId != null);
        String lockKey = orderService.getLockKey(order);
        Object lock = localLockService.getLock(lockKey);

        synchronized (lock) {
            Order orderInDb = orderService.findOrder(shopId, orderId, false);

            //只有后付款的订单才设置
            if (!getBoolean(orderInDb.getPayFirst())) {
                orderInDb.setBusinessEndShow(true);
            }

            //已经在结算的情况下，不能再落单了
            orderService.checkPayingOrder(orderInDb);

            List<OrderItem> orderItemList = orderService.filterOrderItem(orderInDb, getList(order.getOrderItemDetailList()));

            //单个菜品的数量不能超过XX个
            for (OrderItem orderItem : orderItemList) {
                orderService.checkOrderItemMaxNumber(orderItem);
            }

            SceneSwitch sceneSwitch = SceneService.getInstance().sceneSwitch(orderInDb);
            //场景切换了，返回
            if (getBoolean(sceneSwitch.sceneSwitch)) {
                orderService.removeCardItem(orderInDb);
                commonService.save(orderInDb);
                return commonService.createResponse(sceneSwitch);
            }

            //查看场景是否开户了审核，如果需要审核，这里不发送到厨房
            Long sceneId = orderService.getOrderSceneId(orderInDb);
            Optional<Scene> sceneOptional = commonService.searchOneOptional(sceneId, Scene.class);
            VerifyUtil.verify(() -> sceneOptional.isPresent(), SYSTEM_ERROR);
            Scene scene = sceneOptional.get();

            boolean orderKitchenAudit = isOrderKitchenAudit(orderInDb, scene);

            if (config.stockManageOpen()) {
                List<Long> orderItemIdList = orderService.toOrderItemIdList(orderItemList);
                //只处理没有发送厨房的
                List<Long> sendKitchenList = orderService.getSendKitchenList(orderInDb);
                orderItemIdList = orderItemIdList.stream().filter(orderItemId -> !sendKitchenList.contains(orderItemId)).collect(Collectors.toList());

                List<Stock> stockList = StockService.getInstance().confirmOrder(orderInDb, orderItemIdList, !orderKitchenAudit && !getBoolean(orderInDb.getPayFirst()));
                if (isNotEmpty(stockList)) {
                    logger.info("lock result is {}", stockList);
                    return commonService.createResponse(stockList);
                }
            }

            //这里一定是C端在调用，如果是后付款模式，需要发送到厨房
            if (!getBoolean(orderInDb.getPayFirst())) {
                List<Long> printOrderItemIdList = getList(orderInDb.getSendPrinterList()).stream().map(p -> p.getOrderItemId()).collect(Collectors.toList());
                //过滤出没有发送到厨房的列表
                List<Long> orderItemIdList = getList(order.getOrderItemDetailList()).stream().map(p -> p.getId()).filter(orderItemId -> {
                    return !printOrderItemIdList.contains(orderItemId.longValue());
                }).collect(Collectors.toList());

                //需要审核，把审核的数据记录下来
                if (orderKitchenAudit) {
                    if (isNotEmpty(order.getOrderItemDetailList())) {
                        List<OrderItem> itemList = orderService.filterOrderItemList(orderInDb, orderItemIdList);
                        itemList.forEach(orderItem -> orderItem.setCheckout(true));
                        order.setOrderItemDetailList(itemList);
                        order.setOrderType(orderInDb.getOrderType());
                        orderService.generateSendToKitchenAudit(order, orderInDb);

                        int countConfirmOrder = orderService.countConfirmOrder(shopId);
                        WebSocketService.getInstance().sendKitchenOrderAudit(shopId, countConfirmOrder);

                        //先将菜设置成已checkout
                        orderService.filterOrderItemList(orderInDb, orderItemIdList).forEach(orderItem -> orderItem.setCheckout(true));

                        //重新计算价格
                        orderService.calPriceForB_C_END(orderInDb);
                        //特别处理，未确认的订单进行打包费计算
                        orderService.packageFeeCompute(orderInDb);
                    }
                } else {

                    //打印厨房订单日志
                    OrderOpLogService kitchenOrderOpLogService = OrderOpLogService.getInstance();
                    OrderOpLog kitchenOrderOpLog = kitchenOrderOpLogService.content(orderInDb, PRINT_KITCHEN_ORDER);
                    kitchenOrderOpLogService.printKitchenOrder(orderInDb, kitchenOrderOpLog);


                    orderService.confirmOrder(order, orderInDb);

                    //是否需要发送到厨房之后打印整个订单
                    //C端需要发送到厨房后打印，并且真正的有菜已经发送到厨房了，两个人同时落单的情况不打印小票
                    logger.info("orderItemIdList size is {}, scene find {}", orderItemIdList.size(), sceneOptional.isPresent());

                    orderService.calPriceForB_C_END(orderInDb);

                    //保存发送到厨房的列表
                    commonService.save(orderInDb);

                    //同时打印
                    if (isNotEmpty(orderItemIdList) && sceneOptional.isPresent() && getBoolean(sceneOptional.get().getSendToKitchenAutoPrintOrderInC())) {
                        orderService.printOrderTipsSendToKitchen(orderInDb);
                    } else {
                        logger.info("will not print whole order when send to kitchen");
                    }
                }


                //确认落单
                OrderOpLogService kitchenOrderOpLogService = OrderOpLogService.getInstance();
                OrderOpLog kitchenOrderOpLog = kitchenOrderOpLogService.content(orderInDb, CONFIRM_ORDER);
                kitchenOrderOpLogService.confirmOrder(orderInDb, kitchenOrderOpLog);

                //保存发送到厨房的列表
                commonService.save(orderInDb);
            } else {
                List<OrderItem> orderItemDetailListInDb = getList(orderInDb.getOrderItemDetailList());

                //将C端的菜品修改成正在结算
                for (OrderItem orderItem : getList(order.getOrderItemDetailList())) {
                    Optional<OrderItem> orderItemOptional = orderItemDetailListInDb.stream().filter(p -> equals(p.getId(), orderItem.getId())).findAny();
                    if (orderItemOptional.isPresent()) {
                        OrderItem orderItemInDb = orderItemOptional.get();
                        logger.info("will set order item {} to checkout", orderItemInDb.getId());
                        orderItemOptional.get().setCheckout(true);
                        orderItemOptional.get().setConfirmItem(true);
                    } else {
                        logger.info("can not find the order item {}", orderItem.getId());
                    }
                }

                //场景没有切换，余量也是够的,还没有设置过落单时间，那么添加落单时间
                if (orderInDb.getConfirmTime() == null) {
                    orderInDb.setConfirmTime(System.currentTimeMillis());
                }

                //这里是先付款模式，只有餐桌模式才可以在B端显示
                if (orderService.isFoodTableOrder(orderInDb)) {
                    orderInDb.setBusinessEndShow(true);
                }

                //计算一下订单的价格
                orderService.calPriceForB_C_END(orderInDb);

                commonService.save(orderInDb);
            }

            commonService.save(orderInDb);
            return commonService.createResponse(orderInDb);
        }
    }

    private boolean isOrderKitchenAudit(Order orderInDb, Scene scene) {
        boolean findTheSendToKitchenAudit = false;
        Optional<RunModuleScene> moduleSceneOptional = null;
        logger.info("order in db data is : default food table order {}, row {}, column {}, ", orderInDb.getDefaultFoodTableOrder(), orderInDb.getTableRow(), orderInDb.getTableColumn());

        //找到对应的场景处理
        if (getBoolean(orderInDb.getDefaultFoodTableOrder())) {
            //"3是餐桌二维码点单（餐桌上固定的二维码点单），" +
            moduleSceneOptional = getList(scene.getRunModuleSceneList()).stream().filter(p -> p.getType() == 3).findAny();
        } else if (getBoolean(orderInDb.getTableRow() != null && orderInDb.getTableColumn() != null)) {
            // "4是服务员出单二维码点单（拼桌二维码点单），" +
            moduleSceneOptional = getList(scene.getRunModuleSceneList()).stream().filter(p -> p.getType() == 4).findAny();
        } else {
            //   "1是总二维码,C端的总二维码，可以区分堂食和外卖，（C端）"
            moduleSceneOptional = getList(scene.getRunModuleSceneList()).stream().filter(p -> p.getType() == 1).findAny();
        }

        if (moduleSceneOptional != null && moduleSceneOptional.isPresent() && getBoolean(moduleSceneOptional.get().getSendToKitchenAuditPayAfter())) {
            findTheSendToKitchenAudit = true;
        } else {
            logger.info("do not find the scene run module config or config is close");
        }
        return findTheSendToKitchenAudit;
    }

    @ApiOperation(value = "订单合并，必须所有订单都没有在支付")
    @PutMapping(value = ("/order/conbine"), produces = ("application/json;charset=UTF-8"))
    @MethodValidator(duplicationRequestCheck = true)
    public ResponseData combineOrder(@RequestBody CombineOrder combineOrder) throws Exception {
        Long shopId = combineOrder.getShopId();
        List<Long> orderIdList = combineOrder.getOrderIdList();
        VerifyUtil.verify(() -> shopId != null);
        VerifyUtil.verify(() -> isNotEmpty(orderIdList));

        //每个订单，不能有已经支付的订单
        List<Order> orderList = orderService.findOrderList(orderIdList, shopId);

        //订单的数量要相等
        VerifyUtil.verify(() -> orderList.size() == orderIdList.size());

        //每个订单不能有已经支付完成的订单
        List<Boolean> hasPayedList = orderIdList.stream().map(orderId -> orderService.hasPayedOrder(shopId, orderId)).collect(Collectors.toList());

        //存在一个订单则报错
        VerifyUtil.verify(() -> hasPayedList.size() != 0, ResponseStatus.COMBINE_ORDER_BUT_HAS_PAYED_ORDER);

        //返回第一个元素
        Optional<Order> keepFoodTableOrderOptional = orderService.filterFoodTableOrder(orderList);

        //删除订单
        for (Order order : orderList) {
            order.setCombineOrder(true);
            order.setStatus(ORDER_STATUS_END);
            commonService.save(order);

            //后面操作被合并订单那个订单
            if (keepFoodTableOrderOptional.isPresent()) {
                Order keepFoodTableOrder = keepFoodTableOrderOptional.get();
                if (equals(keepFoodTableOrder.getId(), order.getId())) {
                    logger.info("order keep {}", order.getId());
                    continue;
                }
            }

            //删除餐桌,不保留的餐桌删除掉
            List<FoodTable> foodTableList = FoodTableService.getInstance().findFoodTableListByOrder(order);

            for (FoodTable foodTable : foodTableList) {
                logger.info("will delete food table {}", foodTable.getId());
                foodTable.setFlagDelete(false);
                commonService.delete(foodTable);
                //处理模板
                foodTableService.dealTemplateFoodTable(foodTable);
            }
        }

        //合并所有订单成一个订单，并且删除订单
        Order newOrder = orderService.combineOrderList(orderList);

        //处理其它餐桌的状态
        List<Order> subOrderList = orderList.subList(1, orderList.size());
        for (Order order : subOrderList) {
            FoodTable foodTable = FoodTableService.getInstance().findFoodTableByUniqueCode(order.getShopId(), order.getTableRow(), order.getTableColumn(), order.getUniqueCode());

            if (foodTable != null) {
                foodTableService.cleanFoodTable(foodTable);
            }
        }

        return commonService.createResponse(newOrder);
    }

    @ApiOperation(value = "过期订单的退款，并且一定是支付订单，只退钱，其它不处理,")
    @PutMapping(value = ("/order/return/expire/order/cash"), produces = ("application/json;charset=UTF-8"))
    @MethodValidator(duplicationRequestCheck = true)
    public ResponseData returnExpireOrder(@RequestBody Order reqOrder) throws Exception {
        orderService.checkOrderParam(reqOrder);
        orderService.returnExpireOrder(reqOrder);
        return ResponseData.emptyResponse();
    }


    @ApiOperation(value = "订单退款，参数是id,如果是支付订单，那么只退回支付订单的钱，如果是总的订单，那么所有的支付的订单的钱都将退回")
    @PutMapping(value = ("/order/return/cash"), produces = ("application/json;charset=UTF-8"))
    @MethodValidator(duplicationRequestCheck = true)
    public ResponseData returnCash(@RequestBody Order reqOrder) throws Exception {
        Long orderId = reqOrder.getId();
        Long shopId = reqOrder.getShopId();
        VerifyUtil.verify(() -> shopId != null);
        VerifyUtil.verify(() -> orderId != null);

        Object lock = localLockService.getLock(addTwoKey(shopId, orderId));

        synchronized (lock) {
            Order orderInDb = orderService.findOrder(shopId, orderId);

            //必须要是支付订单
            if (!getBoolean(orderInDb.getParentOrder())) {
                //重命令，更好阅读以及维护
                Order subOrder = orderInDb;
                List<Order> payOrderList = orderService.findPayOrderWithSubOrderId(subOrder);

                //必须要是支付完成的订单
                Optional<Order> orderOptional = payOrderList.stream().filter(p -> getBoolean(p.getPayed()) && !getBoolean(p.getCancel())).findAny();
                VerifyUtil.verify(() -> orderOptional.isPresent(), NOT_PAYED_ORDER_CAN_NOT_RETURN_CASH);
                orderInDb = orderOptional.get();

                Optional<DiscountTicket> discountTicketOptional = DiscountTicketService.getInstance().findTicket(orderInDb.getSatisfyAndCutCode(), orderInDb.getCompanyId());
                //优惠券已经不存在了
                if (discountTicketOptional.isPresent()) {
                    DiscountTicket discountTicket = discountTicketOptional.get();
                    VerifyUtil.verify(() -> !getBoolean(discountTicket.getUsed()), ORDER_AFTER_TICKET_USED);
                }

                //如果是会员的等级，判断会员的积分还够退吗
                Long customerId = orderInDb.getCustomerId();
                if (customerId != null) {
                    CustomerService customerService = CustomerService.getInstance();
                    Optional<ScoreHistory> scoreHistoryOptional = customerService.findScoreHistory(customerId, orderInDb.getShopId(), orderInDb.getId());
                    if (scoreHistoryOptional.isPresent()) {
                        ScoreHistory scoreHistory = scoreHistoryOptional.get();
                        int score = getInt(scoreHistory.getValue());
                        Customer customer = customerService.findCustomer(customerId);
                        logger.info("customer score {}, order score {}", customer.getScore(), score);
                        VerifyUtil.verify(() -> getInt(customer.getScore()) >= score, RETURN_CASH_SCORE_NOT_ENOUGH);
                    } else {
                        logger.info("can not find the order");
                    }
                }

                orderService.returnCash(orderInDb);

                //同时修改子订单的状态
                subOrder.setPayed(false);
                subOrder.setStatus(ORDER_STATUS_STARTING);
                subOrder.setTipsFee(null);
                subOrder.setStripeTerminal(false);
                subOrder.setPayedWith3rd(null);

                //只要是退款，不管原来用的是什么支付，都直接setEftPay(false)
                subOrder.setEftPay(false);

                commonService.save(subOrder);

                //父订单，修改支付方式
                List<Order> payedOrderList = orderService.findPayedOrder(subOrder.getShopId(), subOrder.getParentOrderId());
                int payTypeBit = orderService.calPayTypeBit(payedOrderList);
                Order parentOrder = orderService.findOrder(subOrder.getShopId(), subOrder.getParentOrderId());
                parentOrder.setPayTypeBit(payTypeBit);
                parentOrder.setPayTypeNameList(PayTypeService.getInstance().filterOrderPayTypeNameList(parentOrder));

                //只要是退款，不管原来用的是什么支付，都直接setEftPay(false)
                parentOrder.setEftPay(false);

                //父订单重新计算优惠金额
                parentOrder.setDiscountFee(payedOrderList.stream().mapToDouble(p -> getDouble(p.getDiscountFee())).sum());

                commonService.save(parentOrder);

            } else {
                //如果是父订单，那么会取消所有已经支付的订单
                List<Order> payedOrderList = orderService.findPayedOrder(shopId, orderId);
                //不处理失效订单
                payedOrderList = payedOrderList.stream().filter(p -> !getBoolean(p.getExpiredOrder())).collect(Collectors.toList());
                //如果存在會員等級，需要判斷會員的積分是否還足夠
                List<Order> customerOrderList = payedOrderList.stream().filter(o -> o.getCustomerId() != null).collect(Collectors.toList());
                if (isNotEmpty(customerOrderList)) {
                    CustomerService customerService = CustomerService.getInstance();
                    Map<Long, List<Order>> orderMap = customerOrderList.stream().collect(Collectors.groupingBy(o -> o.getCustomerId()));
                    for (Map.Entry<Long, List<Order>> customerOrderEntry : orderMap.entrySet()) {
                        Long customerId = customerOrderEntry.getKey();
                        Customer customer = customerService.findCustomer(customerId);
                        List<Order> customerPayOrderList = customerOrderEntry.getValue();

                        int totalCostScore = customerPayOrderList.stream().mapToInt(o -> {
                            Optional<ScoreHistory> scoreHistoryOptional = customerService.findScoreHistory(customerId, o.getShopId(), o.getId());
                            if (scoreHistoryOptional.isPresent()) {
                                return getInt(scoreHistoryOptional.get().getValue());
                            } else {
                                return 0;
                            }
                        }).sum();

                        VerifyUtil.verify(() -> getInt(customer.getScore()) >= totalCostScore, RETURN_CASH_SCORE_NOT_ENOUGH);
                    }
                }

                //判断有没有消费后金额的券已经使用了
                for (Order payOrder : payedOrderList) {
                    //没有消费后金额的券不处理
                    if (isEmpty(payOrder.getSatisfyAndCutCode())) {
                        continue;
                    }

                    Optional<DiscountTicket> discountTicketOptional = DiscountTicketService.getInstance().findTicket(payOrder.getSatisfyAndCutCode(), payOrder.getCompanyId());
                    //优惠券已经不存在了
                    if (!discountTicketOptional.isPresent()) {
                        continue;
                    }

                    DiscountTicket discountTicket = discountTicketOptional.get();
                    VerifyUtil.verify(() -> !getBoolean(discountTicket.getUsed()), ORDER_AFTER_TICKET_USED);
                }


                for (Order payedOrder : payedOrderList) {
                    //必须要是支付完成的订单
                    VerifyUtil.verify(() -> getBoolean(payedOrder.getPayed()), ResponseStatus.NOT_PAYED_ORDER_CAN_NOT_RETURN_CASH);
                    VerifyUtil.verify(() -> !getBoolean(payedOrder.getCancel()), ResponseStatus.REPEAT_RETURN_CASH);
                    orderService.returnCash(payedOrder);
                }

                //如果父订单绑定了优惠券，那么退回优惠卷
                if (isNotEmpty(orderInDb.getTicketCodeList())) {
                    List<String> ticketCodeList = orderInDb.getTicketCodeList();
                    Shop shop = ShopService.getInstance().findShopById(shopId);
                    for (String code : ticketCodeList) {
                        DiscountTicket discountTicket = DiscountTicketService.getInstance().findTicketByCode(code, shop.getCompanyId());
                        if (!DiscountTicketService.getInstance().isWaiterLeaderTicket(discountTicket)) {
                            discountTicket.setUsed(false);
                            commonService.save(discountTicket);
                        }
                    }
                }

                //删除所有的子订单
                List<Order> allSubOrderList = orderService.findAllSubOrders(shopId, orderId);
                for (Order subOrder : allSubOrderList) {
                    commonService.delete(subOrder, false);
                }

                //总订单要改成可继续下单
                orderInDb.setStatus(ORDER_STATUS_STARTING);

                //如果是一个人退款的时候，保留支付方式
                if (getInt(orderInDb.getUserSelectPayType()) != USER_SELECT_PAY_TYPE_ONE_USER) {
                    orderInDb.setUserSelectPayType(null);
                }

                orderInDb.setPartCash(null);
                orderInDb.setPartCashJson(null);
                orderInDb.setPartFood(null);
                orderInDb.setReturnCash(true);
                orderInDb.setPayed(false);
                orderInDb.setCashPayList(new ArrayList<>());
                orderInDb.setTicketCodeList(new ArrayList<>());
                orderInDb.setParentTicketCodeList(new ArrayList<>());
                orderInDb.setDiscountFee(0d);
                orderInDb.setRealDiscountFee(0d);
                orderInDb.setPayedWith3rd(null);
                orderService.cleanPayType(orderInDb);

                //只要是退款，不管原来用的是什么支付，都直接setEftPay(false)
                orderInDb.setEftPay(false);

                //取消支付方式
                orderInDb.setPayTypeBit(0);
                orderInDb.setPayTypeNameList(new ArrayList<>());

                //重新计算价格
                orderService.calPriceForB_C_END(orderInDb);
                orderInDb.setLeft_cash_b(orderInDb.getUserCost_b());
                orderInDb.setLeft_cash_c(orderInDb.getUserCost_c());
                orderInDb.setTipsFee(null);
                orderInDb.setStripeTerminal(false);
                commonService.save(orderInDb);

            }
            return commonService.createResponse(orderInDb);
        }
    }

    /**
     * 锁定现金或者菜
     *
     * @return
     */
    @ApiOperation(value = "锁定菜或者现金")
    @MethodValidator(mustLogin = false, duplicationRequestCheck = true)
    @PutMapping(value = ("/order/lock/cash/order/item"), produces = ("application/json;charset=UTF-8"))
    public ResponseData lockCashOrOrderItem(@RequestBody LockOrderCashOrOrderItemBo lockOrderCashOrOrderItemBo) throws Exception {
        Long shopId = lockOrderCashOrOrderItemBo.getShopId();
        Long orderId = lockOrderCashOrOrderItemBo.getOrderId();
        VerifyUtil.verify(() -> shopId != null);
        VerifyUtil.verify(() -> orderId != null);

        lockOrderCashOrOrderItemBo.setLockSessionId(ServletUtil.getSessionId());
        lockOrderCashOrOrderItemBo.setLockTime(System.currentTimeMillis());
        String lockKey = orderService.getLockKey(shopId, orderId);
        Object lock = localLockService.getLock(lockKey);

        synchronized (lock) {
            User user = ServletUtil.getUser();

            if (user != null && isNotEmpty(lockOrderCashOrOrderItemBo.getOrderItemIdList())) {
                logger.info("user should not lock the item");
                return ResponseData.emptyResponse();
            }

            Order orderInDb = orderService.findOrder(shopId, orderId);

            if (user == null) {
                //c端请求，判断B端是否锁定了，那么给提示
                logger.info("shop id {}, order id {}", shopId, orderId);
                VerifyUtil.verify(() -> !getBoolean(orderInDb.getLockByWaiter()), ResponseStatus.WAITER_OPERATE_THE_ORDER);
            }

            //如果是现金锁，那么只要判断曾经是否存在
            if (getBoolean(lockOrderCashOrOrderItemBo.lockCash) && getDouble(lockOrderCashOrOrderItemBo.getCash()) == 0) {
                String sessionId = ServletUtil.getSessionId();
                List<LockOrderCashOrOrderItem> lockOrderCashOrOrderItemList = getList(orderInDb.getLockOrderCashOrOrderItemList());
                boolean inTheCashLock = orderService.isInTheCashLock(lockOrderCashOrOrderItemList, sessionId);

                if (inTheCashLock) {
                    logger.info("inTheCashLock {}", sessionId);
                    Optional<LockOrderCashOrOrderItem> cashLockOptional = lockOrderCashOrOrderItemList.stream().filter(p -> equals(p.getLockSessionId(), sessionId)).findFirst();

                    return commonService.createResponse(cashLockOptional.get().getCash());
                }
            }

            //锁钱，如果没有锁定，那么会报错
            if (getBoolean(lockOrderCashOrOrderItemBo.getLockCash()) && getDouble(lockOrderCashOrOrderItemBo.getCash()) == 0) {
                logger.info("lock the cash");
                Double cash = orderService.lockThePartCash(orderInDb);
                return commonService.createResponse(cash);
            } else if (getBoolean(lockOrderCashOrOrderItemBo.getLockCash())) {
                //锁现金，B端不锁现金
                logger.info("lock the customer cash");
                Double lockCash = PartPayService.getInstance().lockTheCustomCash(lockOrderCashOrOrderItemBo, orderInDb);
                //c端自定义分钱日志
                OrderOpLogService orderOpLogService = OrderOpLogService.getInstance();
                OrderOpLog orderOpLog = orderOpLogService.content(orderInDb, PART_CASH_MONEY);
                SubOrder subOrder = new SubOrder();
                subOrder.setUserCost(lockOrderCashOrOrderItemBo.getCash());
                orderOpLogService.PartCashMoneyLog(subOrder, orderOpLog);
                return commonService.createResponse(lockCash);
            } else {
                //锁菜
                logger.info("lock the order item");
                List<Long> lockFailedOrderItemList = orderService.lockTheOrderItem(orderInDb, lockOrderCashOrOrderItemBo);

                if (isNotEmpty(lockFailedOrderItemList)) {
                    logger.info("lock failed order item list");
                    Order order = new Order();
                    BeanUtils.copyProperties(orderInDb, order, "id");
                    List<OrderItem> orderItemList = orderService.filterOrderItemList(order, lockFailedOrderItemList);
                    order.setOrderItemDetailList(orderItemList);
                    order.setLockOrderItemError(true);
                    return commonService.createResponse(order);
                }
                //c端分菜日志
                OrderOpLogService orderOpLogService = OrderOpLogService.getInstance();
                OrderOpLog orderOpLog = orderOpLogService.content(orderInDb, MOVE_ITEM_TO_SUB_ORDER);
                orderOpLogService.partItemPay(orderInDb, lockOrderCashOrOrderItemBo, orderOpLog);

                return commonService.createResponse(orderInDb);
            }
        }
    }

    /**
     * 锁定现金或者菜
     *
     * @return
     */
    @ApiOperation(value = "释放菜或者现金")
    @MethodValidator(mustLogin = false)
//    @DeleteMapping(value = ("/order/lock/cash/order/item"), produces = ("application/json;charset=UTF-8"))
    public ResponseData clearLockCashOrOrderItem(@RequestBody LockOrderCashOrOrderItemBo lockOrderCashOrOrderItemBo) throws Exception {
        Long shopId = lockOrderCashOrOrderItemBo.getShopId();
        Long orderId = lockOrderCashOrOrderItemBo.getOrderId();
        VerifyUtil.verify(() -> shopId != null);
        VerifyUtil.verify(() -> orderId != null);

        lockOrderCashOrOrderItemBo.setLockSessionId(ServletUtil.getSessionId());
        lockOrderCashOrOrderItemBo.setLockTime(System.currentTimeMillis());
        String lockKey = orderService.getLockKey(shopId, orderId);
        Object lock = localLockService.getLock(lockKey);

        synchronized (lock) {
            Order orderInDb = orderService.findOrder(shopId, orderId);
            PartPayService.getInstance().releaseLock(orderInDb);
            return commonService.createResponse(orderInDb);
        }
    }

    @ApiOperation(value = "获取有哪些菜已经被锁定了")
    @MethodValidator(mustLogin = false)
    @PutMapping(value = ("/order/lock/order/item/list"), produces = ("application/json;charset=UTF-8"))
    public ResponseData lockItemList(@RequestBody Order reqOrder) throws Exception {
        Long shopId = reqOrder.getShopId();
        Long orderId = reqOrder.getId();
        VerifyUtil.verify(() -> shopId != null);
        VerifyUtil.verify(() -> orderId != null);

        Order orderInDb = orderService.findOrder(shopId, orderId);
        String sessionId = ServletUtil.getSessionId();

        //先把其它已过期的会话消除掉
        PartPayService.getInstance().filterExpireOrder(orderInDb);
        commonService.save(orderInDb);

        //已经支付了，或者其它用户已经锁定了
        List<LockOrderCashOrOrderItem> list = getList(orderInDb.getLockOrderCashOrOrderItemList()).stream().filter(lockOrderCashOrOrderItem -> !equals(lockOrderCashOrOrderItem.getLockSessionId(), sessionId) || getBoolean(lockOrderCashOrOrderItem.getPayed())).collect(Collectors.toList());

        List<Long> orderItemIdList = list.stream().flatMap(lockItem -> getList(lockItem.getOrderItemIdList()).stream()).collect(Collectors.toList());

        return commonService.createResponse(orderItemIdList);
    }

    @ApiOperation(value = "锁定支付方式")
    @MethodValidator(mustLogin = false, duplicationRequestCheck = true)
    @PutMapping(value = ("/order/lock/order/pay/type"), produces = ("application/json;charset=UTF-8"))
    public ResponseData lockPayType(@RequestBody Order order) throws Exception {
        orderService.checkOrderParam(order);
        String lockKey = orderService.getLockKey(order);
        Object lock = localLockService.getLock(lockKey);

        synchronized (lock) {
            int lockResult = orderService.lockPayType(order);

            //锁定支付方式成功，还是外卖，或者快餐，或者是餐桌先付款，那么一个人锁定
            if (lockResult == LOCK_ORDER_PAY_TYPE_OK && orderService.isOnlyOneUserCanPayInC(order)) {
                Order orderInDb = orderService.findOrder(order.getShopId(), order.getId());
                //成功锁定了，那么添加一个人锁定所有金额
                LockOrderCashOrOrderItemBo lockOrderCashOrOrderItemBo = new LockOrderCashOrOrderItemBo();
                lockOrderCashOrOrderItemBo.setShopId(orderInDb.getShopId());
                lockOrderCashOrOrderItemBo.setOrderId(orderInDb.getId());
                lockOrderCashOrOrderItemBo.setLockCash(true);
                lockOrderCashOrOrderItemBo.setCash(formatMoneyDouble(orderInDb.getUserCost()));
                PartPayService.getInstance().oneUserPayLock(lockOrderCashOrOrderItemBo, orderInDb);

                //修改订单状态为一个人付款
                orderInDb.setStatus(ORDER_STATUS_ONE_PERSON_PAYING);
                orderInDb.setUserSelectPayType(USER_SELECT_PAY_TYPE_ONE_USER);
                commonService.save(orderInDb);
            }

            return commonService.createResponse(lockResult);
        }
    }


    @ApiOperation(value = "添加锁定时间")
    @MethodValidator(mustLogin = false, duplicationRequestCheck = true)
    @PutMapping(value = ("/order/add/lock/time/pay"), produces = ("application/json;charset=UTF-8"))
    public ResponseData addLockTime(@RequestBody LockOrderCashOrOrderItemBo lockOrderCashOrOrderItemBo) throws Exception {
        VerifyUtil.verify(() -> lockOrderCashOrOrderItemBo.getShopId() != null);
        VerifyUtil.verify(() -> lockOrderCashOrOrderItemBo.getOrderId() != null);
        VerifyUtil.verify(() -> lockOrderCashOrOrderItemBo.getPayType() != 0);


        PartPayService.getInstance().addLockTime(lockOrderCashOrOrderItemBo.getShopId(), lockOrderCashOrOrderItemBo.getOrderId(), lockOrderCashOrOrderItemBo.getPayType());
        return ResponseData.emptyResponse();
    }

    /**
     * 获取用户选择的支付方式
     * <p>
     * 这里会保证C端的订单，如果不是餐桌后付款模式，一定会有是一个人付款模式
     * 如果是在B端的订单，外卖和快餐均不校验，由后面的流程保证
     *
     * @param orderInDb
     * @return
     * @throws ResponseStatusException
     */
    public int getUserSelectPayType(Order orderInDb) throws Exception {
        Long userId = ServletUtil.getUserId();

        //如果是C端的请求，不是餐桌后付款模式，那么都是一个人付款
        if (userId == null && orderService.isOnlyOneUserCanPayInC(orderInDb)) {
            return USER_SELECT_PAY_TYPE_ONE_USER;
        } else {
            return getInt(orderInDb.getUserSelectPayType());
        }
    }

    @ApiOperation(value = "结账的时候，判断余量是否足够")
    @PutMapping(value = ("/check/order/stock/available"), produces = ("application/json;charset=UTF-8"))
    public ResponseData isStockAvailable(@RequestBody Order reqOrder) throws Exception {
        Long shopId = reqOrder.getShopId();
        VerifyUtil.verify(() -> shopId != null);

        if (isNotEmpty(reqOrder.getIds())) {
            List<Long> ids = reqOrder.getIds();

            for (Long orderId : ids) {
                String lockKey = orderService.getLockKey(shopId, orderId);
                Object lock = localLockService.getLock(lockKey);

                synchronized (lock) {
                    List stockList = checkOrderStockAvailable(shopId, orderId);
                    if (isNotEmpty(stockList)) {
                        return commonService.createResponse(stockList);
                    }
                }
            }

            //都是可用的
            return commonService.createResponse(new ArrayList<>());
        } else {
            Long orderId = reqOrder.getId();
            VerifyUtil.verify(() -> orderId != null);

            String lockKey = orderService.getLockKey(shopId, orderId);
            Object lock = localLockService.getLock(lockKey);

            synchronized (lock) {
                List list = checkOrderStockAvailable(shopId, orderId);

                //仓库有问题
                if (isNotEmpty(list)) {
                    return commonService.createResponse(list);
                }

                //库存没有问题，那么查看是否要添加子订单
                orderService.addSubOrders(orderService.findOrder(shopId, orderId));

                return commonService.createResponse(list);

            }
        }
    }

    /**
     * 判断单个订单是否可用
     *
     * @param shopId
     * @param orderId
     * @return
     * @throws Exception
     */
    private List checkOrderStockAvailable(Long shopId, Long orderId) throws Exception {
        //B端，如果从未发送过厨房，结账的时候，也要先判断余量和售罄
        //这里判断锁定量跟余量的判断关系，以及菜品是否售罄了
        Order orderInDb = orderService.findOrder(shopId, orderId);
        List<Stock> stockListResult = null;

        //已经在结账了，不用再判断了
        if (Arrays.asList(ORDER_STATUS_PART_CASH_PAYED, ORDER_STATUS_PART_FOOD_PAYED, ORDER_STATUS_PART_CASH_CUSTOMIZED_PAYED, ORDER_STATUS_END).contains(getInt(orderInDb.getStatus()))) {
            return new ArrayList<>();
        }

        StockService stockService = StockService.getInstance();
        stockListResult = stockService.checkNonLockedOrderItem(orderInDb);

        //锁定量为空的OrderItem，需要锁定的时候，发现余量不够了
        if (isNotEmpty(stockListResult)) {
            StockService.getInstance().formatAvailableCount(stockListResult);
            return stockListResult;
        }

        List<OrderItem> orderItemList = orderService.filterLockedOrderItem(orderInDb);
        stockListResult = StockService.getInstance().checkOrderItemListStock(orderItemList);

        StockService.getInstance().formatAvailableCount(stockListResult);
        return stockListResult;
    }

    @ApiOperation(value = "创建一个VIP续费的订单,要传shop_id, customerId,如果要传几个月或者几年传vipRenewTime，")
    @MethodValidator(mustLogin = false, duplicationRequestCheck = true)
    @PutMapping(value = ("/user/order/vip"), produces = ("application/json;charset=UTF-8"))
    public ResponseData createVipOrder(@RequestBody Order order) throws Exception {
        order.setVipOrder(true);
        Long shopId = order.getShopId();
        Long companyIdInOrder = order.getCompanyId();

        VerifyUtil.verify(() -> shopId != null || companyIdInOrder != null);
        Long customerId = order.getCustomerId();

        Long companyId = companyIdInOrder;
        CustomerService customerService = CustomerService.getInstance();

        if (companyIdInOrder == null) {
            Company company = CompanyService.getInstance().findCompanyByShopId(shopId);
            companyId = company.getId();
        }

        Customer customer = customerService.findCustomer(customerId);
        long vipEndTime = System.currentTimeMillis();

        if (customer.getVipEndTime() != null) {
            vipEndTime = customer.getVipEndTime();
        }

        vipEndTime = DateUtil.endTimeInDay(vipEndTime);

        //在现有的VIP结束时间之后添加时间
        Optional<VipRenewMode> vipRenewModeOptional = customerService.findVipRenewMode(companyId);

        //不存在
        if (!vipRenewModeOptional.isPresent()) {
            VerifyUtil.throwError(VIP_RENEW_NOT_CONFIG);
        }

        VipRenewMode vipRenewMode = vipRenewModeOptional.get();
        Integer period = vipRenewMode.getPeriod();
        if (period == null) {
            VerifyUtil.throwError(VIP_RENEW_NOT_CONFIG);
        }

        Integer vipTime = order.getVipRenewTime();
        if (vipTime == null) {
            logger.info("add default vip time");
            vipTime = 1;
        }

        if (period == VIP_RENEW_YEAR) {
            Date date = new Date(vipEndTime);
            Date vipEndDate = DateUtil.addYear(date, vipTime);
            order.setVipEndTime(vipEndDate.getTime());
        } else {
            Date date = new Date(vipEndTime);
            Date vipEndDate = DateUtil.addMonth(date, vipTime);
            order.setVipEndTime(vipEndDate.getTime());
        }

        logger.info("the order is {}", order);
        //计算费用
        Double renewFee = customerService.getRenewFee(vipRenewMode);
        VerifyUtil.verify(() -> renewFee > 0, VIP_RENEW_NOT_CONFIG);
        order.setUserCost(showMoneyDouble(renewFee * vipTime));
        order.setUserCost_b(order.getUserCost());
        order.setUserCost_c(order.getUserCost());
        order.setUserCostLong(showMoneyDouble(getDouble(order.getUserCost()) * 100).longValue());
        order.setOnlinePay(true);
        order.setReturnCash(false);
        order.setParentOrder(false);

        Integer orderNo = orderService.createOrderNo(shopId);
        order.setOrderNo(orderNo);
        return commonService.createObject(order);
    }

    @ApiOperation(value = "落单审核查询")
    @PutMapping(value = ("/send/to/kitchen/audit"), produces = ("application/json;charset=UTF-8"))
    @MethodValidator(checkShopId = true)
    public ResponseData searchSendToKitchenAudit(@RequestBody SendToKitchenAudit sendToKitchenAudit) throws Exception {
        return commonService.searchResponse(sendToKitchenAudit, (sendToKitchenAuditInDb) -> {
            //必须要数量大于0
            List<OrderItem> orderItemList = sendToKitchenAuditInDb.getOrderItemDetailList().stream().filter(orderItem -> getInt(orderItem.getCount()) > 0).collect(Collectors.toList());
            sendToKitchenAuditInDb.setOrderItemDetailList(orderItemList);
        });
    }

    @ApiOperation(value = "落单审核修改")
    @PutMapping(value = ("/send/to/kitchen/audit/modify"), produces = ("application/json;charset=UTF-8"))
    @MethodValidator(checkShop = true, duplicationRequestCheck = true)
    public ResponseData modifySendToKitchenAudit(@RequestBody SendToKitchenAudit sendToKitchenAudit) throws Exception {
        SendToKitchenAudit sendToKitchenAuditInDb = commonService.findEntity(sendToKitchenAudit.getId(), SendToKitchenAudit.class);
        //锁定订单之后再操作
        String lockKey = orderService.getLockKey(sendToKitchenAuditInDb.getShopId(), sendToKitchenAuditInDb.getOrderId());
        Object lock = localLockService.getLock(lockKey);

        synchronized (lock) {
            //订单已被删掉
            VerifyUtil.verify(() -> !getBoolean(sendToKitchenAuditInDb.getFlagDelete()), ORDER_DELETED);

            //查询订单，如果订单已经开始结账了，那么不能再操作了
            Long shopId = sendToKitchenAuditInDb.getShopId();
            Long orderId = sendToKitchenAuditInDb.getOrderId();
            Order orderInDb = orderService.findOrderCanFlagDelete(shopId, orderId);

            //要么没有开始结账，要么是C端选择现金结账
            VerifyUtil.verify(() -> orderInDb.getStatus() == ORDER_STATUS_STARTING || orderService.oneUserPayAndSelectCash(orderInDb), SEND_TO_KITCHEN_AUDIT_ERROR);

            //这次是同意
            if (sendToKitchenAuditInDb.getStatus() != SEND_TO_KITCHEN_AUDIT_OK && sendToKitchenAudit.getStatus() == SEND_TO_KITCHEN_AUDIT_OK) {
                //同步修改订单里面菜的数量
                sendToKitchenAuditInDb.getOrderItemDetailList().stream().forEach(orderItem -> {
                    Optional<OrderItem> orderItemOptional = orderInDb.getOrderItemDetailList().stream().filter(orderItemInOrderDb -> {
                        return orderItemInOrderDb.getId().longValue() == orderItem.getId().longValue();
                    }).findAny();

                    if (orderItemOptional.isPresent()) {
                        orderItemOptional.get().setCount(orderItem.getCount());
                    } else {
                        logger.info("can not find the order item {}", orderItem.getId());
                    }
                });

                //过滤掉已经删除的
                List<OrderItem> orderItemList = orderInDb.getOrderItemDetailList().stream().filter(orderItem -> orderItem.getCount() > 0).collect(Collectors.toList());
                orderInDb.setOrderItemDetailList(orderItemList);

                //保存订单里面的数量
                commonService.save(orderInDb);


                //过滤掉已经删除的
                orderItemList = sendToKitchenAuditInDb.getOrderItemDetailList().stream().filter(orderItem -> orderItem.getCount() > 0).collect(Collectors.toList());
                sendToKitchenAuditInDb.setOrderItemDetailList(orderItemList);

                orderService.confirm(sendToKitchenAuditInDb);

                //重新计算价格并保存
                orderService.calPriceAndSave_b_c(sendToKitchenAuditInDb);

                orderService.showPrice(orderInDb, true);

                //同意落单
                OrderOpLogService opLogService = OrderOpLogService.getInstance();
                OrderOpLog orderOpLog = opLogService.content(orderInDb, AGREE_BE_ALONE);
                opLogService.agreeBeAlone(sendToKitchenAuditInDb, orderOpLog);

                Order cloneOrder = (Order) ClassUtil.clone(orderInDb);

                List<OrderItem> cloneOrderItemList = cloneOrder.getOrderItemDetailList().stream().filter(orderItem -> orderItem.getCheckout() && orderItem.getConfirmItem()).collect(Collectors.toList());

                cloneOrder.setOrderItemDetailList(cloneOrderItemList);
                //如果需要打印小票，则打印小票
                orderService.printOrderTipsSendToKitchen(cloneOrder);
            } else if (sendToKitchenAuditInDb.getStatus() != SEND_TO_KITCHEN_AUDIT_REJECT && sendToKitchenAudit.getStatus() == SEND_TO_KITCHEN_AUDIT_REJECT) {
                orderService.rejectAudit(orderInDb, sendToKitchenAuditInDb);

                commonService.save(orderInDb);

                //拒绝落单
                OrderOpLogService opLogService = OrderOpLogService.getInstance();
                OrderOpLog orderOpLog = opLogService.content(orderInDb, REFUSE_BE_ALONE);
                opLogService.refuseBeAlone(sendToKitchenAuditInDb, orderOpLog);
            } else {
                logger.info("the audit status is {}, db status is {}", sendToKitchenAudit.getStatus(), sendToKitchenAuditInDb.getStatus());
            }

            int confirmOrder = orderService.countConfirmOrder(sendToKitchenAuditInDb.getShopId());
            WebSocketService.getInstance().sendKitchenOrderAudit(sendToKitchenAuditInDb.getShopId(), confirmOrder);

            ResponseData responseData = commonService.modifyObject(sendToKitchenAudit);
            return responseData;
        }
    }

    @ApiOperation(value = "落单审核细节修改,传ID,shopId, orderId以及对应的修改")
    @PutMapping(value = ("/send/to/kitchen/audit/item/modify"), produces = ("application/json;charset=UTF-8"))
    @MethodValidator(checkShopId = true, duplicationRequestCheck = true)
    public ResponseData modifySendToKitchenAuditItem(@RequestBody SendToKitchenItemAudit sendToKitchenItemAudit) throws Exception {
        Long shopId = sendToKitchenItemAudit.getShopId();
        Long orderId = sendToKitchenItemAudit.getOrderId();
        VerifyUtil.verify(() -> shopId != null);
        VerifyUtil.verify(() -> orderId != null);

        //锁定订单之后再操作
        String lockKey = orderService.getLockKey(shopId, orderId);
        Object lock = localLockService.getLock(lockKey);

        synchronized (lock) {
            //查询订单，如果订单已经开始结账了，那么不能再操作了
            Order orderInDb = orderService.findOrderCanFlagDelete(shopId, orderId);
            VerifyUtil.verify(() -> orderInDb.getStatus() == ORDER_STATUS_STARTING, SEND_TO_KITCHEN_AUDIT_ERROR);

            SendToKitchenAudit sendToKitchenAuditInDb = commonService.findEntity(sendToKitchenItemAudit.getId(), SendToKitchenAudit.class);

            //菜的ID是需要校验的
            Long orderItemId = sendToKitchenItemAudit.getOrderItemId();
            VerifyUtil.verify(() -> orderItemId != null);

            //不是删除操作，那么就是更新操作
            if (!sendToKitchenItemAudit.isDeleteItem()) {
                int modifyCount = sendToKitchenItemAudit.getCount();
                VerifyUtil.verify(() -> modifyCount < 0);
                Optional<OrderItem> orderItemOptional = sendToKitchenAuditInDb.getOrderItemDetailList().stream().filter(orderItem -> orderItem.getId().longValue() == orderItemId.longValue()).findAny();
                if (orderItemOptional.isPresent()) {
                    OrderItem orderItem = orderItemOptional.get();
                    //减了之后，还有余量
                    if (orderItem.getCount() + modifyCount > 0) {
                        orderItem.setCount(orderItem.getCount() + modifyCount);
                        commonService.save(sendToKitchenAuditInDb);
                    } else {
                        //减了之后，没有余量了
                        orderItemOptional.get().setCount(0);
                        commonService.save(sendToKitchenAuditInDb);
                    }
                    //减少订单审核里面的orderItem
                    OrderOpLogService opLogService = OrderOpLogService.getInstance();
                    OrderOpLog foodTableContent = opLogService.content(orderInDb, Constant.SUB_AUDIT_ORDER_ITEM);
                    opLogService.subOrderAuditOrderItem(orderItem, foodTableContent);
                } else {
                    //已经删除过了，找不到了，并发的时候可能会出现
                    logger.info("order item id {} deleted", orderItemId);
                }
            } else {
                //删除操作不校验数量,直接过滤掉删除
                Optional<OrderItem> orderItemOptional = sendToKitchenAuditInDb.getOrderItemDetailList().stream().filter(orderItem -> orderItem.getId().longValue() == orderItemId.longValue()).findAny();
                if (orderItemOptional.isPresent()) {
                    //删除订单审核里面的orderItem
                    OrderOpLogService opLogService = OrderOpLogService.getInstance();
                    OrderOpLog foodTableContent = opLogService.content(orderInDb, Constant.DELETE_AUDIT_ORDER_ITEM);
                    opLogService.deleteOrderAuditOrderItem(orderItemOptional.get(), foodTableContent);
                    orderItemOptional.get().setCount(0);
                    commonService.save(sendToKitchenAuditInDb);
                } else {
                    logger.info("can not find the order item {}", orderItemId);
                }
            }

            //全部的数量都改成了0
            if (sendToKitchenAuditInDb.getOrderItemDetailList().stream().allMatch(orderItem -> orderItem.getCount() <= 0)) {
                sendToKitchenAuditInDb.setStatus(SEND_TO_KITCHEN_AUDIT_REJECT);
                orderService.rejectAudit(orderInDb, sendToKitchenAuditInDb);
                commonService.save(orderInDb);
                commonService.save(sendToKitchenAuditInDb);

                //socket通知
                int confirmOrder = orderService.countConfirmOrder(sendToKitchenAuditInDb.getShopId());
                WebSocketService.getInstance().sendKitchenOrderAudit(sendToKitchenAuditInDb.getShopId(), confirmOrder);

                //重新计算价格,并保存
                orderService.calPriceAndSave_b_c(sendToKitchenAuditInDb);
            }

            //重新计算审核的价格
            //reCalSendToKitchenAuditPrice(orderInDb, sendToKitchenAuditInDb);

            //只显示有数据的
            List<OrderItem> orderItemList = sendToKitchenAuditInDb.getOrderItemDetailList().stream().filter(orderItem -> {
                return orderItem.getCount() > 0;
            }).collect(Collectors.toList());
            sendToKitchenAuditInDb.setOrderItemDetailList(orderItemList);

            return commonService.createResponse(sendToKitchenAuditInDb);
        }
    }

    /**
     * 重新计算审核单的价格
     *
     * @param orderInDb
     * @param sendToKitchenAuditInDb
     * @throws Exception
     */
    private void reCalSendToKitchenAuditPrice(Order orderInDb, SendToKitchenAudit sendToKitchenAuditInDb) throws Exception {
        Order priceOrder = new Order();
        priceOrder.setShopId(orderInDb.getShopId());
        priceOrder.setId(orderInDb.getId());
        priceOrder.setOrderItemDetailList(sendToKitchenAuditInDb.getOrderItemDetailList());
        double sum = sendToKitchenAuditInDb.getOrderItemDetailList().stream().mapToDouble(p -> p.getCount() * p.getItemPrice()).sum();
        sendToKitchenAuditInDb.setCash(showMoneyDouble(sum));
        commonService.save(sendToKitchenAuditInDb);
    }

    @ApiOperation(value = "修改小费,传shopId,id")
    @PutMapping(value = ("/order/tips/modify"), produces = ("application/json;charset=UTF-8"))
    @MethodValidator(checkShopId = true, duplicationRequestCheck = true)
    public ResponseData modifyTips(@RequestBody Order order) throws Exception {
        Long orderId = order.getId();
        Long shopId = order.getShopId();
        Double tipsFee = order.getTipsFee();
        VerifyUtil.verify(() -> orderId != null);
        VerifyUtil.verify(() -> shopId != null);
        VerifyUtil.verify(() -> tipsFee != null && tipsFee >= 0);

        String lockKey = orderService.getLockKey(order);

        Object lock = localLockService.getLock(lockKey);

        synchronized (lock) {
            Order orderInDb = orderService.findOrder(shopId, orderId);
            Double tipsFeeInDb = orderInDb.getTipsFee();
            orderInDb.setTipsFee(tipsFee);
            Double userCost_b = orderInDb.getUserCost_b();
            orderInDb.setUserCost_b(userCost_b - getDouble(tipsFeeInDb) + tipsFee);
            orderInDb.setUserCost(orderInDb.getUserCost());

            CommonService.getInstance().save(orderInDb);

            //如果是父订单，查询对应的一个支付订单
            if (getBoolean(orderInDb.getParentOrder())) {
                List<Order> orderList = orderService.findOrderByParentId(orderInDb.getId(), orderInDb.getShopId());
                orderList = orderList.stream().filter(o -> getBoolean(o.getPayed()) && !getBoolean(o.getReturnCash())).collect(Collectors.toList());

                logger.info("payed order list size {}", orderList.size());
                //多于一个单，或者只有一个单
                if (orderList.size() != 1) {
                    VerifyUtil.throwError(SYSTEM_ERROR);
                }

                Order payedOrder = orderList.get(0);
                Double userCost = payedOrder.getUserCost();
                payedOrder.setUserCost(userCost - getDouble(payedOrder.getTipsFee()) + tipsFee);
                payedOrder.setUserCost_b(payedOrder.getUserCost());
                payedOrder.setTipsFee(tipsFee);
                commonService.save(payedOrder);
            }

            return commonService.createResponse(orderInDb);
        }
    }

}
